文章目录
webpack性能优化
1. 代码分离
Webpack中常用的代码分离有三种:
1.1 入口起点
使用entry配置手动分离代码;也就是多入口文件来实现代码分离;
注意:多入口起点的话就要注意重复引入,导致体积过大的问题,可配置共享解决此问题
module.exports = {
mode: 'development',
devtool: false,
// entry: './src/index.js',
entry: {
index: {
import: './src/index.js',
dependOn: 'shared' //可共享
},
main: {
import: './src/main.js',
dependOn: 'shared' //可共享
},
shared: ['axios'] //配置共享的模块
},
output: {
path: path.resolve(__dirname, './build'),
// placeholder
filename: '[name]-bundle.js', //[name]也就是entry对应的名称
clean: true //每次打包的前先清理之前的打包文件
},
}
1.2 自定义分包
另外一种分包的模式是splitChunk,它底层是使用SplitChunksPlugin来实现的:
因为该插件webpack已经默认安装和集成,所以我们并不需要单独安装和直接使用该插件;
只需要提供SplitChunksPlugin相关的配置信息即可;
// 优化配置
optimization: {
// 设置生成的chunkId的算法
// development: named
// production: deterministic(确定性)
// 早期webpack4中使用: natural
chunkIds: 'deterministic',
// runtime的代码是否抽取到单独的包中(早Vue2脚手架中)
runtimeChunk: {
name: "runtime"
},
// 分包插件: SplitChunksPlugin
splitChunks: {
chunks: "all",//包括异步引入和同步引入,而async只仅仅针对异步的
// 当一个包大于指定的大小时, 继续进行拆包
maxSize: 20000,//当分出来的包又大于20000,则继续拆包
// // 将包拆分成不小于minSize的包
minSize: 10,//最小的包体积
// 自己对需要进行拆包的内容进行分包
cacheGroups: {
utils: {
test: /utils/,
filename: "[id]_[hash:6]_utils.js" //hash是哈希值
},
vendors: {
// /node_modules/
// window上面 /\
// mac上面 /
test: /[\\/]node_modules[\\/]/,
filename: "[id]_vendors.js"
}
}
},
// 代码优化: TerserPlugin => 让代码更加简单 => Terser
minimizer: [
// JS代码简化
new TerserPlugin({
extractComments: false
})
// CSS代码简化
]
},
1.3 动态导入
通过模块的内联函数调用来分离代码;(动态路由)
1.3.1 两种实现动态导入的方式:
第一种,使用ECMAScript中的 import() 语法来完成,也是目前推荐的方式;
第二种,使用webpack遗留的 require.ensure,目前已经不推荐使用;
比如我们有一个模块 bar.js:
该模块我们希望在代码运行过程中来加载它(比如判断一个条件成立时加载);
因为我们并不确定这个模块中的代码一定会用到,所以最好拆分成一个独立的js文件;
这样可以保证不用到该内容时,浏览器不需要加载和处理该文件的js代码;
这个时候我们就可以使用动态导入;
1.3.2 动态导入的文件命名
[name]是文件路径各个层级组合而成的名称,默认是有点丑的
output: {
clean: true,
path: path.resolve(__dirname, './build'),
// placeholder
filename: '[name]-bundle.js',
// 单独针对分包的文件进行命名,[name]是路径名称
chunkFilename: '[name]_chunk.js'
},
引入的时候命名;以通过magic comments(魔法注释)的方式
import(/* webpackChunkName: "about" */'./router/about').then(res => {
res.about()
res.default()
})
2. 预加载preload和预获取prefetch
2.1 区别
- preload chunk 会在父 chunk 加载时,以并行方式开始加载。prefetch chunk 会在父 chunk 加载结束后开始加载。
- preload chunk 具有中等优先级,并立即下载。prefetch chunk 在浏览器闲置时下载。
- preload chunk 会在父 chunk 中立即请求,用于当下时刻。prefetch chunk 会用于未来的某个时刻。
2.2 如何使用?
预加载和预获取不是在配置文件配置的,是在代码引入的时候配置的;如下
btn2.onclick = function() {
import(
/* webpackChunkName: "category" */
/* webpackPrefetch: true */
'./router/category')
}
3. cdn
3.1 配置
output: {
clean: true,
path: path.resolve(__dirname, './build'),
// placeholder
filename: '[name]-bundle.js',
// 单独针对分包的文件进行命名
chunkFilename: '[name]_chunk.js',
// publicPath: 'http://coderwhycdn.com/'
},
3.2 html中使用
<script src="https://cdn.bootcdn.net/ajax/libs/axios/1.2.0/axios.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
3.2 排除打包
因为使用的是cnd,则排除打包
// 排除某些包不需要进行打包
externals: {
react: "React",
// key属性名: 排除的框架的名称
// value值: 从CDN地址请求下来的js中提供对应的名称
axios: "axios"
},
3.3 推荐较为知名的cdn服务器
国际上使用比较多的是unpkg、JSDelivr、cdnjs;
国内也有一个比较好用的CDN是bootcdn;
4. 提取css
如果用的是css-loader和style-loder,那么css代码是集成到js文件里面的,不是单独的css;需要做如下的配置;
4.1 插件安装
npm install mini-css-extract-plugin -D
4.2 使用
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const TerserPlugin = require('terser-webpack-plugin')
const { ProvidePlugin } = require('webpack')
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
module.exports = {
mode: 'development',
devtool: false,
// entry: './src/index.js',
entry: './src/main.js',
output: {
clean: true,
path: path.resolve(__dirname, './build'),
// placeholder
filename: 'js/[name]-bundle.js',
// 单独针对分包的文件进行命名
chunkFilename: 'js/[name]_chunk.js',
// publicPath: 'http://coderwhycdn.com/'
},
resolve: {
extensions: ['.js', '.json', '.wasm', '.jsx', '.ts']
},
devServer: {
static: ['public', 'content'],
port: 3000,
compress: true,
proxy: {
'/api': {
target: 'http://localhost:9000',
pathRewrite: {
'^/api': ''
},
changeOrigin: true
}
},
historyApiFallback: true
},
// 优化配置
optimization: {
// 设置生成的chunkId的算法
// development: named
// production: deterministic(确定性)
// webpack4中使用: natural
chunkIds: 'deterministic',
// runtime的代码是否抽取到单独的包中(早Vue2脚手架中)
runtimeChunk: {
name: "runtime"
},
// 分包插件: SplitChunksPlugin
splitChunks: {
chunks: "all",
// 当一个包大于指定的大小时, 继续进行拆包
// maxSize: 20000,
// // 将包拆分成不小于minSize的包
// minSize: 10000,
minSize: 10,
// 自己对需要进行拆包的内容进行分包
cacheGroups: {
utils: {
test: /utils/,
filename: "js/[id]_utils.js"
},
vendors: {
// /node_modules/
// window上面 /\
// mac上面 /
test: /[\\/]node_modules[\\/]/,
filename: "js/[id]_vendors.js"
}
}
},
// 代码优化: TerserPlugin => 让代码更加简单 => Terser
minimizer: [
// JS代码简化
new TerserPlugin({
extractComments: false
})
// CSS代码简化
]
},
module: {
rules: [
{
test: /\.jsx?$/,
use: {
loader: "babel-loader",
}
},
{
test: /\.ts$/,
use: 'babel-loader'
},
{
test: /\.css$/,
use: [
// 'style-loader', 开发阶段
MiniCssExtractPlugin.loader, // 生产阶段
'css-loader'
]
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './index.html'
}),
// 完成css的提取
new MiniCssExtractPlugin({
filename: 'css/[name].css',
chunkFilename: 'css/[name]_chunk.css'
})
]
}
5. js压缩丑化代码Terser(减少体积)
在webpack中有一个minimizer属性,在production模式下,默认就是使用TerserPlugin来处理我们的代码的;
如果我们对默认的配置不满意,也可以自己来创建TerserPlugin的实例,并且覆盖相关的配置;
相关文档
https://github.com/terser/terser#compress-options
https://github.com/terser/terser#mangle-options
5.1 安装
npm install terser -D
5.2 使用
首先,我们需要打开minimize,让其对我们的代码进行压缩(默认production模式下已经打开了)
其次,我们可以在minimizer创建一个TerserPlugin:
- extractComments:默认值为true,表示会将注释抽取到一个单独的文件中;
✓ 在开发中,我们不希望保留这个注释时,可以设置为false; - parallel:使用多进程并发运行提高构建的速度,默认值是true
✓ 并发运行的默认数量: os.cpus().length - 1;
✓ 我们也可以设置自己的个数,但是使用默认值即可; - terserOptions:设置我们的terser相关的配置
✓ compress:设置压缩相关的选项;
✓ mangle:设置丑化相关的选项,可以直接设置为true;
✓ toplevel:顶层变量是否进行转换;
✓ keep_classnames:保留类的名称;
✓ keep_fnames:保留函数的名称;
minimize: true,
// 代码优化: TerserPlugin => 让代码更加简单 => Terser
minimizer: [
// JS压缩的插件: TerserPlugin
new TerserPlugin({
extractComments: false,
terserOptions: {
compress: {
arguments: true,
unused: true
},
mangle: true,
// toplevel: false
keep_fnames: true
}
}),
// CSS压缩的插件: CSSMinimizerPlugin
new CSSMinimizerPlugin({
// parallel: true
})
]
6. css压缩
css-minimizer-webpack-plugin
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
new CSSMinimizerPlugin({
// parallel: true
})
7. css tree Shaking
用来清除未使用的css样式
npm install purgecss-webpack-plugin -D
const glob = require('glob')
const { PurgeCSSPlugin } = require('purgecss-webpack-plugin')
plugins: [
// 完成css的提取
new MiniCssExtractPlugin({
filename: 'css/[name].css',
chunkFilename: 'css/[name]_chunk.css'
}),
// 对CSS进行TreeShaking
new PurgeCSSPlugin({
paths: glob.sync(`${path.resolve(__dirname, '../src')}/**/*`, { nodir: true }),
safelist: function() {
return {
standard: ["body"]
}
}
})
]
8 文件压缩gzip
npm install compression-webpack-plugin -D
plugins: [
// 完成css的提取
new MiniCssExtractPlugin({
filename: 'css/[name].css',
chunkFilename: 'css/[name]_chunk.css'
}),
// 对CSS进行TreeShaking
// new PurgeCSSPlugin({
// paths: glob.sync(`${path.resolve(__dirname, '../src')}/**/*`, { nodir: true }),
// safelist: function() {
// return {
// standard: ["body"]
// }
// }
// }),
// 作用域提升
new webpack.optimize.ModuleConcatenationPlugin(),
// 对打包后的文件(js/css)进行压缩
new CompressionPlugin({
test: /\.(js|css)$/,
algorithm: 'gzip'
})
]