小知识,大挑战!本文正在参与「程序员必备小知识」创作活动
本文已参与 「掘力星计划」 ,赢取创作大礼包,挑战创作激励金。
引言
本文是weback学习的进阶篇,主要讲解webpack优化方面常用的Tree-Shaking和Code Splitting,关于基础篇请看这里:前端工程化之Webpack---基础篇
Tree-Shaking
Tree-Shaking的本意是摇掉没有用到的代码,即Dead Code Elimination(DCE),它最开始是有rollup提出的,
例如我们在math.js中导出两个方法add、minus
// math.js
export function add(a, b) {
return a + b
}
export function minus(a, b) {
return a - b
}
然后在index.js中导入add方法并使用
// index.js
import { add } from './math'
add(1,2)
// webpack.config.js
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
module.exports = {
mode: 'development',
devtool: 'cheap-module-source-map',
entry: {
main: './src/index.js'
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /.(jpg|png|gif)$/,
use: {
loader: 'url-loader',
options: {
name: '[name]_[hash].[ext]',
outputPath: 'images/'
}
}
},
{
test: /.css$/,
use: [
'style-loader',
'css-loader'
]
},
{
test: /\.js$/,
use: [
'loader1',
'loader2',
{
loader: 'loader3',
options: {
name: 'alexis'
}
}
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new CleanWebpackPlugin()
],
resolveLoader: {
modules: [
'node_modules',
path.resolve(__dirname, 'loaders')
]
},
optimization: {
usedExports: true
}
}
然后在package.json中加入以下字段
"sideEffects": false
在控制台运行npx webpack,可以看到使用tree-shaking前后生成的bundle.js
可以看到webpack在harmony export字段中提示我们用到了add,而没有用到(unused)minus模块,并且两段代码中都包含了add和minus的相关代码,这是因为我们在开发环境下tree-shaking不会删除没有用到的代码,删除的话可能会影响scource-map的映射,如果我们将代码打包上线(production)的话,tree-shaking就会将dead code删除掉,并且如果我们将mode字段值改为prodction的话,webpack内置的优化会自动将tree-shaking相关配置设定好,我们甚至可以不用添加optimization
注意:Tree-Shaking只支持ES Module的引入方式,不支持CommonJS的引入方式,也就是说Tree-Shaking只能检测import导入的模块,对于require引入的模块则不能检测
Code Splitting
假如在开发中,业务代码中一小个片段修改了,比如只修改了一个变量名,而webpack需要打包生成整个文件,并且用户需要重新下载整个资源文件,造成资源/性能浪费,而我们经常会引入一些公共类库,这些库的修改频率是非常低的,因此我们有必要分离公共类库,让多页面利用缓存,从而减少下载的代码量。
Code Splitting本质上与Webpack没有任何关系,只不过webpack的一些插件可以很快帮我们实现code splitting
const path = require('path')
module.exports = {
mode: 'development',
devtool: 'eval—cheap-source-map',
entry: {
main: './src/index.js'
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
// ...
]
},
plugins: [
// ...
],
optimization: {
usedExports: true,
splitChunks: {
chunks: "async", // 表示选择哪些 chunks 进行分割,可选值有:async,initial和all
minSize: 30000, // 表示新分离出的chunk必须大于等于minSize,默认为30000,约30kb。
minChunks: 1, // 表示一个模块至少应被minChunks个chunk所包含才能分割。默认为1。
maxAsyncRequests: 5, // 表示按需加载模块时,同时请求的最大数目。默认为5,如果超过5个则不会做代码分割
maxInitialRequests: 3, // 表示加载入口文件时,同时请求的最大数目。默认为3。
automaticNameDelimiter: '~', // 表示拆分出的chunk的名称连接符。默认为~。如chunk~vendors.js
name: true, // 设置chunk的文件名。默认为true。当为true时,splitChunks基于chunk和cacheGroups的key自动命名。
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10,
filename: 'vendor.js'
},
default: {
priority: -20,
reuseExistingChunk: true, //
filename: 'common.js'
}
}
}
}
}
关于cacheGroups:cacheGroups下可以可以配置多个缓存组,每个组根据
test设置条件,符合test条件的模块,就分配到该组。模块可以被多个组引用,如果两个组的条件都符合,最终会根据priority来决定打包到哪个组中,值越大优先级越高。默认将所有来自 node_modules目录的模块打包至vendors组,将两个以上的chunk所共享的模块打包至default组。 运行npx webpack可以看到dist目录下多生成了一个vendor.js文件
这是因为我们对optimization作了相应配置,webpack会自动将外部库文件和业务代码分离,自动为我们实现code splitting