基本配置
1 | 安装 |
1 | # 命令行打包 |
1 | // webpack.config.js |
补充:绝对路径拼接
引入nodejs的path.resolve方法,借助__dirname
拼接路径
__dirname
为当前文件所在目录的绝对路径(e.g. 即webpack.config.js所在路径)
注意使用__dirname
无需声明或引号
path.resolve:ref
- 从右往左构建路径,直到遇到绝对路径片段则停止
- 若没有遇到绝对路径片段,则前面自动添加当前文件所在目录的绝对路径(即等同于
__dirname
) - 若参数为空,则等同于
__dirname
Entry
entry的值有以下三种类型 ref
string:打包后形成一个chunk(默认名为main),输出一个bundle文件(即单入口)
array:多入口,同string只生成一个chunk,输出一个bundle
(即多入口的js文件,即使没有引用关系,也会打包到同一个文件中。通常用于解决hrm中html热更新问题)
object:多入口,形成多个chunk和输出多个bundle,chunk名称即为object中的key
(object里面的value可以为string或array,为array时即类似于2,将多个不相关的js打包到同一文件,如下面例子将react相关库打包到同一文件)
1
2
3entry: {
react: ['react', 'react-dom', 'react-router'],
}
webpack5默认值:entry: './src/index.js'
Output
filename:文件路径(optional)+ 名称
命名过程可以使用如下特殊变量(用中括号
[]
表示)[name]
: (仅针对object类型的多入口entry):entry object中的key名- hash相关(包括
[hash]
,[chunkhash]
,[contenthash]
,[contenthash:10]
等)
path:输出文件目录(后续所有资源都会输出到该目录,需要是绝对路径)
publicPath:(适用于生产环境,为所有资源添加一个公共路径前缀)
chunkFilename:区别于filename,只对非入口chunk生成的bundle命名,同理可以使用1中的特殊变量
通过动态import的模块会生成单独的chunk/bundle
(注意:默认
[name]
为根据id自动生成,也可以按如下方式在import中指定:import(/* webpackChunkName: 'myModule */'./path/to/myModule.js').then()
)通过optimization.splitChunks对node_module模块进行分割后的chunk
library相关:library和libraryTarget:将模块/库暴露出来供后续调用
library: 设置模块对应的变量名(对于多入口可以使用
[name]
即chunk名)libraryTarget:设置挂载到的目标对象,可选
'window' | 'global' | 'commonjs' | 'umd'
等(一般结合dll使用)
补充:webpack5默认值
1 | output: { |
Resolve
可以给路径添加一个别名前缀(e.g. ~
, $css
等)
后续import时可以使用这个别名,而无需通过相对路径的方式引入文件(e.g 多重../../
)
1 | resolve: { |
Loaders
css相关loader
1 | 安装 |
1 | rules: [ |
补充: outputPath
在loader的options中可设置outputPath: 'yourOutputFolderName'
,可以将对应的文件输出到对应output文件夹的不同路径(e.g. img, media, data, etc)
处理图片
1 | 安装 (url-loader依赖于file-loader所以需要一起安装,使用只需要url-loader) |
1 | rules: [ |
补充:base64将图片编码为字符串,可以减少文件请求数量但增大传送数据体积,一般折中选择小于8-12kb的图片转为base64
eslint
以使用airbnb eslint为例
配置airbnb eslint:
1
npm i -D eslint eslint-plugin-import eslint-config-airbnb-base
1
2
3
4
5
6
7// package.json对象添加如下属性:
"eslintConfig": {
"extends": "airbnb-base",
"env": {
"browser": true
}
}配置eslint loader
1
npm i -D eslint-loader eslint # eslint-loader依赖eslint,如果前面安装了eslint则此处无需安装
1
2
3
4
5
6
7
8{ // 添加loader
test: /\.js$/,
loader: 'eslint-loader',
exclude: /node_modules/, // 排除node_modules文件夹
options: {
fix: true, // 自动修复
},
},
babel
1 | npm i -D babel-loader @babel/core |
基本js兼容性处理:使用@babel/preset-env配置文件
1
npm i -D @babel/preset-env
1
2
3
4
5
6
7
8{ // 注意:需要放在eslint等loader的下面,或者设置enfore: "pre"优先执行
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'], // 预设:指示babel做怎样的兼容性处理
}
}存在问题:只能转换基本语法,promise等高级语法无法转换
全部兼容性处理:使用@babel/polyfill
1
npm i -D @babel/polyfill
1
2// 在入口文件index.js中引入
import '@babel/polyfill';按需加载兼容性处理:使用core-js
1
npm i -D core-js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
options: {
cacheDirectory: true, // optional: 开启babel缓存,用于性能优化
presets: [
['@babel/preset-env',
{ // 按需加载
useBuiltIns: 'usage',
corejs: {
version: 3, //指定core-js版本
},
targets: { // 兼容目标浏览器版本
chrome: '60',
firefox: '60',
ie: '9',
safari: '10',
edge: '17'
}
}]
],
}
},
Plugins
html-webpack-plugin
复制一个模板html文件(或默认创建一个空html文件),并引入打包好的资源
1 | 安装 |
1 | const HtmlWebpackPlugin = require('html-webpack-plugin') |
mini-css-extract-plugin
将css提取出成单独文件(而不是集合到js中再添加到style标签)以优化性能
1 | 安装 |
1 | const MiniCssExtractPlugin = require("mini-css-extract-plugin") |
optimize-css-assets-webpack-plugin
压缩css
1 | npm i -D optimize-css-assets-webpack-plugin |
1 | const OptimizeCssAssetsWebpackPlugin = require("optimize-css-assets-webpack-plugin"); |
devServer
1 | devServer: { |
Optimization
参考基本配置:
1 | const TerserWebpackPlugin = require("terser-webpack-plugin"); |
性能优化总结
开发环境优化:
HRM
hot module replacement 热替换: 在devServer中,当一个模块变化是,只重新打包该模块(提升构建速度)
- devServer里面配置
hot: true
- 对于style文件,style-loader默认实现了HRM
- 对于html文件:需要在webpack.config.js添加
entry: ['./src/js/index.js', './src/index.html']
,否则热更新会失效
SourceMap
添加从源码到编译后代码的映射,方便从console中找出源码出错位置
需要在webpack.config中添加devtool: ‘source-map’
缓存
babel缓存:配置loader的option,添加cacheDirectory: true
文件资源缓存:配置output.filename: ‘path/to/bundle.[hash:10].js’,其中hash值替换为以下选项(即contenthash)
- hash: webpack构建时生成的文件hash值,每次构建俊辉变化
- chunkhash:根据不同入口文件生成的打包文件具有不同hash值
- contenthash:根据文件内容生成的hash值,文件发生变化则hash值发生变化
设置为contenthash后,不变化的文件生成的打包文件,其文件名中的hash值不变,因此可以有效利用web缓存
生产环境优化
Tree Shaking
去掉无用代码(开启production mode自动启用)
Code Split
- 使用多入口进行代码分割,适用于多页面应用(较少使用)
1 | module.exports = { |
- 使用split chunks分割打包node_modules:
- 对于单入口打包,使用splitChunks会将所有引用的node_modules打包到一个vendor文件
- 对于多入口打包,如果多个入口使用共同的模块,会单独抽出来进行打包
1 | module.exports = { |
其他optimization配置参考 ref
- 对于引用本地自行创建的模块,可以通过动态加载的方式将引入的模块单独打包
(即在使用时候动态import()模块,该import返回一个promise。注意该功能基于ES6,需要使用babel转换)
1 | // 使用特殊的注释语法给单独打包的模块文件重命名 |
webpackPrefetch为true时即预加载,会在触发异步事件前先将模块加载到本地,默认false(即懒加载)则会等到触发异步事件后才加载模块
预加载会等其他资源加载完毕,等浏览器空闲了才开始加载
PWA
使用workbox (workbox-webpack-plugin 插件) 实现离线加载访问 (在生产环境下)
1 | 安装 |
1 | // webpack.config.js 注册插件 |
1 | // index.js 入口文件注册插件 |
thread-loader
实现多进程打包,加快打包速度
(注意:因为进程启动需要一定时间(大约600ms),因此仅大型项目打包才能体现优化效果)
1 | npm i -D thread-loader |
1 | // webpack.config.js |
externals
对于一些外部引入的全局js库 (e.g. jquery通过cdn引入),可以使用externals配置,webpack不对其进行打包
1 | // webpack.config.js |
dll
和externals的区别:同样可以减少外部库的打包,dll提供了外部引用的库只需打包一次(创建一个单独的打包文件),在本地进行serve;而externals直接忽略外部的库,需要通过cdn进行引入
总结
开发环境优化:
- 打包速度:HMR
- 代码调试:source-map
生产环境优化
- 打包速度:oneOf,babel缓存,多进程打包(thread-loader),externals,ddl
- 运行性能(重点):缓存(hash/chunkhash/contenthash)/ tree-shaking / code split / 动态加载(懒加载和预加载) / pwa