webpack 的使用总结
目录
为什么要用构建工具
- 转换 ES6 的语法
- 转换 JSX
- css 预处理器、前缀补全
- 代码压缩混淆
- 图片、字体等资源的处理
构建工具的演变
由于 requirejs/seajs 等模块化的概念不断催生,前端模块化编写方式越来越复杂
主要使用的构建工具
grunt => gulp => webpack / rollup
grunt 本质上是一个 task runder,将构建过程分为一个个任务,解析 html/css/js 等。但会将每个任务的结果存到本地磁盘,导致打包速度比较慢,磁盘 IO 的操作。
gulp 也是任务流,但是每一步构建的结果不会存放到本地磁盘,而是放在内存中,速度比较快。
webpack
rollup 适合纯 js 的比较小的库,速度快,配置方便。
主要配置项
1 | module.exports = { |
entry
打包文件入口。
1
2
3
4
5
6
7
8
9
10
11// 单入口
module.exports = {
entry: './src/search.js',
}
// 多入口
module.exports = {
entry: {
index: './src/index.js',
search: './src/search.js',
},
}output
打包文件的输出。
1
2
3
4
5
6module.exports = {
output: {
path: path.join(__dirname, 'dist'), // 输出目录
filename: '[name].js', // 输出文件名称(entry 中指定的key名称)
},
}loader
webpack 开箱即用只支持 JS 和 JSON 两种文件类型,通过 Loader 去支持其它文件类型并且把它们转化成有效的模块,并且可以添加到依赖图中。
1
2
3
4
5
6
7
8module: {
rules: [
{
test: /.js$/,
use: 'babel-loader',
},
]
}常用的 loader
plugin
插件⽤于 bundle ⽂文件的优化,资源管理理和环境变量量注⼊入,作⽤于整个构建过程。
- 常用的插件
- 常用的插件
mode
mode ⽤用来指定当前的构建环境是:production、development 还是 none。
一些常用配置
解析 ES6
借助 babel-loader + .babelrc,将 es6 语法转换成 es5 的语法,兼容旧的浏览器。
- webpack
1 | module: { |
- .babelrc
1 | { |
解析 css
- css-loader 用于加载 .css 文件,转换成 commonjs 对象,这样就可以在代码中 import 或者 require 文件了。
- style-loader 将样式通过
<style>
标签插⼊入到 head 中。 - less-loader / sass-loader 转换 less 或者 scss 语法。
1 | module: { |
解析图片、字体
- file-loader 用于处理图片、字体等文件。
1 | module: { |
- url-loader 也可以用于处理图片、字体等文件,并且可以设置较小的资源自动转换成 base64,以减少 http 资源请求。
1 | module: { |
解析 .vue
vue loader 会解析 vue 文件,将 <template>
的内容转换为字符串,插入到 vue 的 template 属性中,并将生成的 js、css 交给下一步 babel-loader 和 css-loader 等去处理。
1 | module.exports = { |
文件监听
- webpack –watch => 当文件有更新时,会自动构建,但不会刷新浏览器,需要手动。
webpack 会轮询判断文件的最后编辑时间是否变化,某个文件发⽣了变化,并不会⽴刻告诉监听者,而是先缓存起来,等 aggregateTimeout 时间后再执行。
1 | module.export = { |
热更新
- 方法一:webpack-dev-server 插件,有三个优势:
- 不刷新浏览器
- 不输出文件,放在内存中,没有磁盘 IO
package.json
1 | "scripts": { |
webpack.config
1 | module.exports = { |
- 方法二:webpack-dev-middleware 中间件。适⽤于灵活的定制场景,需要自己实现服务器(WDM 将 webpack 输出的⽂件传输给服务器)。
使用 express
1 | const express = require('express') |
- 热更新原理
启动阶段:
- 经过 webpack compiler 打包文件;
- 打包好后,将编译好的 bundle 传输给 bundle sever,bundle sever 是一个服务,让浏览器可以正常访问;
更新阶段:
- 当文件发生更新时,webpack compiler 打包文件;
- 代码发送到 HMR server,得知那些模块发生了改变;
- HMR server 通知 HMR runtime,通常以 json 数据传输,HMR runtime 更新浏览器代码,不需要刷新浏览器。
文件指纹
文件指纹一般用来做版本管理,对于没有修改的文件,可以继续取浏览器缓存。例如, 让 index.html 入口的 response header 设置为 cache-control: no-cache, 不允许缓存, 其他 js 文件设置缓存 cache-control: xxx。
- Hash:和整个项目的构建相关,只要项⽬文件有修改,整个项⽬构建的 hash 值就会更更改。
- Chunkhash:和 webpack 打包的 chunk 有关,不同的 entry 会生成不同的 chunkhash 值。
- Contenthash:根据文件内容来定义 hash,文件内容不变,则 contenthash 不变。
1 | module.exports = { |
常用占位符:
html / css / js 压缩
- js 压缩: 内置了 uglifyjs-webpack-plugin 插件,不需要额外处理(当然也可以手动加一些配置)。
- html 压缩:html-webpack-plugin,可以把空格、换行符、注释等都处理掉。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17plugins: [
//多页多入口时,一个 html 页面加一次
new HtmlWebpackPlugin({
template: path.join(__dirname, 'src/index.html'),
filename: 'index.html', // 打包出来的文件名
chunks: ['index'], // 指定打包出来的 html 要写入哪些 chunks
inject: true, // css 等会注入到 html 里面去
minify: {
html5: true,
collapseWhitespace: true,
preserveLineBreaks: false,
minifyCSS: true,
minifyJS: true,
removeComments: false,
},
}),
] - css 压缩:optimize-css-assets-webpack-plugin,同时使⽤用 cssnano
1
2
3
4
5
6plugins: [
new OptimizeCSSAssetsPlugin({
assetNameRegExp: /\.css$/g,
cssProcessor: require('cssnano'),
}),
]
一些进阶配置
自动清理构建目录
每次构建的时候不会清理⽬录,造成构建的输出⽬录 output 文件越来越多,使用 clean-webpack-plugin 清理,默认会清理 output 指定的输出目录。
1 | plugins: [new CleanWebpackPlugin()] |
css 前缀
由于市面上存在不同的浏览器渲染内核,需要为一些 css 属性增加前缀做兼容。如:
1 | .box { |
postCss 是一个后处理器,结合 autoprefixer,生成需要的浏览器 css 前缀。
1 | module.exports = { |
移动端 px 转换成 rem
移动设备尺寸太多了,如果使用媒体查询实现布局会编写很多套,会比较麻烦。
使用 rem ,参考标准是根元素的 font-size。 root element font-size。
使用 px2rem-loader 自动转换成 rem。
- 使用 px2rem-loader 自动转换成 rem
1 | module.exports = { |
- 利用手机淘宝的库:lib-flexible, 自动设置根元素的 font-size
资源内联
将 css 和 js 代码内联到 html,意义:
代码层⾯:
- 页面框架的初始化脚本
- 上报相关打点
- css 内联,避免页面闪动
请求层⾯面:
- 减少 HTTP ⽹络请求数
- 小图⽚或者字体内联 (url-loader)
html / js 内联:
1
2
3
4
5raw-loader 内联 html
// 如 meta 信息
<script>${require('raw-loader!babel-loader!./meta.html')}</script>
raw-loader 内联 JS
<script>${require('raw-loader!babel-loader!../node_modules/lib-flexible')}</script>css 内联:
1 | module.exports = { |
多页面打包方案
- 每个⻚面对应一个 entry,⼀个写 html-webpack-plugin(缺点:每次新增或删除⻚面需要改 webpack 配置)
- 动态获取 entry 和设置 html-webpack-plugin 数量。利用 glob.sync。
1
entry: glob.sync(path.join(__dirname, './src/*/index.js'))
source-map
source-map 的作用是通过 source map 定位到源代码,开发环境开启,线上环境关闭,线上排查问题的时候可以将 sourcemap 上传到错误监控系
提取公共资源
每个页面(多页非单页)可能会使用相同的模块,不需要重复打包了。
1 | module.exports = { |
Tree shaking
webpack 借鉴了 rollup。用于擦除无用代码:
- 代码不会被执行,不可到达
- 代码执⾏的结果不会被用到
- 代码只会影响死变量(只写不读)
例如:
1 | if (false) { |
必须是 es6 import 语法。原理利用了 ES6 模块的特点:
- 只能作为模块顶层的语句句出现
- import 的模块名只能是字符串串常量量
- import binding 是 immutable 的
像 import() 动态引入的代码,只能在执行的时候才知道有没有用到,不能运用。
代码擦除: uglify 阶段删除⽆用代码
scope hoisting
打包后的 es5 代码,为了模拟模块作用域,会存在大量的闭包。
存在问题:
- ⼤量作⽤域包裹代码,导致体积增大(模块越多越明显)
- 运行代码时创建的函数作⽤域变多,内存开销变大
解决原理:
将所有模块的代码按照引用顺序放在一个函数作⽤域里,然后适当的重命名一些变量以防⽌变量名冲突。
通过 scope hoisting 可以减少函数声明代码和内存开销。
webpack mode 为 production 默认开启。
动态引入
脚本懒加载,使得初始下载的代码更小。
ES6: 动态 import() (⽬前还没有原⽣支持,需要 babel 转换)
- .babelrc:
1
2
3{
"plugins": ["@babel/plugin-syntax-dynamic-import"],
}
优化构建时的命令显示
设置 stat
使用 friendly-errors-webpack-plugin (stats 设置成 errors-only)
1 | module.exports = { |
echo$ 可以输出是否打包出错。
compiler 在每次构建结束后,会在 plugin 触发 done 的钩子,所以可以监听 done 捕获:
1 | module.exports = { |
案例:使用 webpack 打包基础库
实现⼀个⼤整数加法库的打包,效果:
- 压缩版本和非压缩版本(分别用于开发和产线)
- ⽀持 AMD/CJS/ESM 模块引⼊
目录结构
模块语法
暴露
webpack 配置:
1 | const TerserPlugin = require('terser-webpack-plugin') |
新增入口文件,判断不同环境使用的入口:
1 | if (process.env.NODE_ENV === 'production') { |
package.json 配置,将 index 设置为 入口, prepublish 钩子设置打包。
1 | { |
webpack 配置设计
webpack 构建速度和体积优化
在 node 中使用 webpack 回调查看打包结果
使用 speed-measure-webpack-plugin 分析打包速度
使用 webpack-bundle-analyzer 查看打包结果
多进程/多实例构建 => thread-loader / HappyPack
多进程并行压缩代码 => parallel-uglify-plugin / uglifyjs-webpack-plugin 开启 parallel 参数 / terser-webpack-plugin 开启 parallel 参数
增加构建缓存(提升二次构建速度)
- babel-loader 开启缓存
- terser-webpack-plugin 开启缓存
- 使用 cache-loader 或者 hard-source-webpack-plugin