实现webpack5在开发环境和生产环境的打包优化详解
前言
本文主要记录我自己在对公司内部项目进行webpack打包做了哪些优化,主要按照开发环境(development)和生产环境(production)进行说明。
个人认为,在开发环境需要的是速度快,需要构建速度快、热更新时间快、重启项目快,以此来达到开发效率上的提升。而对于生产环境,需要的是打包出来的项目所占用的空间要尽量的小,但同时也要考虑打包出来的静态资源的数量不能太多,因为考虑到请求资源的时候要尽量减少请求数量,这需要自己权衡一下,然后再是考虑打包时间。
本人第一次写文章若写得不好还请谅解。
开发环境
Devtool
不同的 devtool
设置,会使打包过程中花费的性能产生差异,webpack官方文档对设置不同的devtool
在花费时间上有做描述,这里就不展示了。对于devtool的选择我选择的是eval-cheap-module-source-map
,代码出错时它的错误信息能确定到具体某一行,我认为已经足够使用。
示例:
devtool: 'eval-cheap-module-source-map'
optimization
在开发环境中最好不要自己去配置optimization
中的代码分割(splitChunks)或者是代码压缩之类的代码优化(minimizer),因为在开发环境中这些基本没有意义,只会增加你开发环境下打包代码、热更新的时间,没有特别需求就不用去配置它,因为在mode为development的时候webpack对它有默认值,而这个默认值对于开发环境基本就是最优的配置了。
loader
同样的,在开发环境中最好不要去配置多余的loader或者更加耗时的loader,因为这些loader也会增加webpack打包时间。比如对于css的处理,在开发环境中用style-loader引进文件使用就行,不需要用MiniCssExtractPlugin.loader
将css单独抽离到一个文件。
然后就是最好确定loader作用的文件,使用include
指定需要该loader去解析的文件或者使用exclude
去排除不需要该loader解析的文件。
对于非常耗时的loader,还可以使用thread-loader
去加快对它的解析,thread-loader
可以将非常消耗资源的 loader 分流给一个 worker pool,但需要注意的是,在为某个loader分配thread-loader
的时候,thread-loader
本身也是会占用一部分启动开销的。或者某些loader有自己的缓存配置项,去配置缓存,加快第二次打包时的速度。
示例
{
test: /\.m?js$/,
include: [path.resolve(__dirname, "../src")],
exclude: [
/node_modules\/core-js\//,
/@babel(?:\/|\\{1,2})runtime|core-js/,
],
use: [
{
loader: "thread-loader",
},
{
loader: "babel-loader",
options: {
cacheDirectory: true,
},
},
],
},
plugin
减少对于开发环境不必要的plugin,与loader一样,plugin也是会占用性能的,如果只对生产环境有用,对开发环境影响不大的plugin,那你可以考虑把它从开发环境中删除掉。
devserver
不要将writeToDisk
设置为true,不要将devserver启动后的文件写入硬盘
cache
webpack5已支持设置缓存,在你第一次打包后,会在打包完成后的空余时间,将打包文件写入node_module的.cache中,在下次打包时会优先读取cache内的文件再决定是否重新打包。这个设置会大大加快再次打包的时间
示例
cache:{
type:"filesystem"
},
生产环境
在生产环境中,主要优化的是减少请求数、占用资源空间大小、减少单个打包的chunk文件过大,tree shaking。
devtool
生产环境中一般可以不设置devtool,webpack的默认值是false,也就是不会生成sourcemap文件,而且也建议在生产环境中不要去设置sourcemap,一是减少代码体积,而是别人可能会通过map文件去窃取你的源码。
减少请求数
在项目中引入某个图片或者字体图标的时候,无论图片和图标多大,都会额外去发送一个请求去获取资源,这对于后台服务器或多或少都会增加一些负担。我们可以通过webpack的配置,将小的图片或者小的字体通过base64的形式插入到js文件中,这样在请求js文件的时候,浏览器解析到需要展示图片就不需要额外去请求一次资源。
示例
module: {
rules: [
{
test:/\.(png|jpe?g|gif|svg|woff2?|eot|ttf|otf)$/,
type: "asset",
include: [path.resolve(__dirname, "../src")],
parser: {
//转base64的条件
dataUrlCondition: {
maxSize: 5 * 1024, // 10kb
}
},
generator:{
filename:'images/[name].[contenthash:6][ext]'
},
},
]
}
压缩
可压缩的分为以下几类文件,js代码压缩、css代码压缩、html压缩、图片压缩
js代码压缩
这里主要使用的是webpack内置的TerserPlugin
,不需要额外安装。webpack5默认在mode为production的时候有开启js压缩的,可以不用去设置。我的配置如下:
optimization:{
minimizer: [
new TerserPlugin({
parallel: true,//使用多进程并发运行以提高构建速度 Boolean|Number 默认值: true
terserOptions: {
compress: {
drop_console: true,//移除所有console相关代码;
drop_debugger: true,//移除自动断点功能;
pure_funcs: ["console.log", "console.error"],//配置移除指定的指令,如console.log,alert等
},
format: {
comments: false,//删除注释
},
},
extractComments: false,//是否将注释剥离到单独的文件中
})
]
}
css压缩
首先需要安装mini-css-extract-plugin
将文件内的css单独抽离出来成一个css文件,然后安装mini-css-extract-plugin
对css文件进行压缩,配置如下:
module: {
rules: [
{
test: /\.css$/,
use: [
{
loader: MiniCssExtractPlugin.loader, // creates style nodes from JS strings
},
{
loader: "css-loader",
},
{
loader: "postcss-loader", // translates CSS into CommonJS
},
],
},
]
}
plugins: [
new MiniCssExtractPlugin({
filename: "css/[name].[contenthash:6].css"
}),
new CssMinimizerPlugin()
]
html压缩
直接通过对html-webpack-plugin
的配置进行压缩,配置如下:
new HtmlWebpackPlugin({
name: page.name,
title: "",
filename: `html/${page.name}.html`,
template: page.template,
inject: true,
chunks: [page.name],
cache: true, // 当文件没有发生任何改变时, 直接使用之前的缓存
minify: {
collapseWhitespace: true,
keepClosingSlash: true,
removeComments: true,
removeRedundantAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
minifyCSS: true,
minifyJS: {
mangle: {
toplevel: true
}
}
}
})
图片压缩
需要安装image-webpack-loader
,配置如下:
module: {
rules: [
{
test:/\.(png|jpg|jpeg|gif|svg)$/,
type: "asset",
include: [path.resolve(__dirname, "../src")],
parser: {
//转base64的条件
dataUrlCondition: {
maxSize: 5 * 1024, // 10kb
}
},
generator:{
filename:'images/[name].[contenthash:6][ext]'
},
use: [
{
loader: "image-webpack-loader"//压缩图片
}
]
},
]
}
splitChunks
splitChunks的作用主要功能是提取重复使用的代码,将大文件代码分割成小的chunk,这样的目的是为了减少代码体积,然后避免单个js文件过大造成长时间的阻塞,导致页面白屏时间增加。这里给出示例配置,详细看注释:
optimization: {
splitChunks: {
chunks: "all",
minSize: 30 * 1024, // 30KB chunk文件的最小值
//maxSize: 2 * 1024 * 1024, //1M
minChunks: 2,//最小引入次数
maxAsyncRequests: 10, // 按需加载的最大并行请求数目
maxInitialRequests: 5, // 在入口点的并行请求的最大数目
name: "default",
cacheGroups: { //根据设置的test匹配特定的依赖将该代码分割出去
default: false,
vendor: {
name: "vendor",
test: /node_modules/,
chunks: "all",
priority: 10,//优先级
},
mintui: {
name: "mint-ui",
test: /mint-ui/,
chunks: "all",
priority: 20,
},
elementui: {
name: "element-ui",
test: /element-ui/,
chunks: "all",
priority: 20,
}
}
},
}
tree shaking
简单来说tree shaking就是将没有使用到的代码、没有引用到的模块给过滤掉,减少代码打包体积。在生产环境中,webpack已经默认为我们开启了对js代码的tree shaking了,而对css的tree shaking需要我们自己去配置。
首先需要安装插件purgecss-webpack-plugin
,在plugins里去使用它,注意的是这个插件也可能将你自己的一些需要用到的样式或者第三方的ui库的样式也给删除掉,所以你需要为其设置白名单。配置如下:
plugins: [
new PurgeCSSPlugin({
paths: glob.sync(`${resolveApp("./src")}/**/*`, {nodir: true}),
// safelist: {
// standard: [/^el-/,"body", "html"], // 过滤以el-开头的类名,哪怕没用到也不删除
// },
extractors: [{
extractor: PurgeFromHtml,
extensions: ['html', "vue"]
}],
whitelist: ["html", "body"],
whitelistPatterns: [
/-(leave|enter|appear)(|-(to|from|active))$/,
/^(?!(|.*?:)cursor-move).+-move$/,
/^router-link(|-exact)-active$/,
/data-v-.*/,
/class/,
/^el-/
],
whitelistPatternsChildren: [/^token/, /^pre/, /^code/, /^el-/]
}),
]
gzip
开启gzip的压缩,这个可以在nginx里去配置,也可以在webpack中开启。在webpack中开启需要安装一个插件
compression-webpack-plugin
,在plugin中使用它:
const CompressionPlugin = require('compression-webpack-plugin');
...
new CompressionPlugin({
test: /\.(css|js)$/i,
threshold: 0,
minRatio: 0.8,
algorithm: "gzip",
// exclude
// include
}),
配置cdn
在项目中,你可以将一些第三方依赖通过cdn的方式引入,来减少你最终代码打包的体积。比如项目中我使用了dayjs,那么需要将你的cdn地址通过script标签引入到html中,然后在webpack中配置externals:
html:
<body>
<div id="app"></div>
<div id="root"></div>
<input type="text" id="">
<!-- ejs中的if判断 -->
<% if (process.env.NODE_ENV === 'production') { %>
<script src="https://unpkg.com/dayjs@1.8.21/dayjs.min.js"></script>
<% } %>
<script>
const message = "Hello World";
console.log(message);
</script>
</body>
webpack:
externals: {
dayjs: "dayjs"
},
转载自:https://juejin.cn/post/7212893989033443387