Vue-Cli4+webpack4项目构建打包优化
前言
这个项目是三年前的产品了,一个业务办理系统,集成了很多的模块,日积月累之下,项目变得臃肿,构建时间也来到了120秒;所以就开始了优化。那看完这片文章,我将告诉你,哪些方法可以提高构建速度?哪些不行?哪些是vue-cli4和webpack4自带的,哪些需要你下载额外的插件。
减少打包体积
代码压缩
uglifyjs-webpack-plugin
代码压缩可以减少构建打包后的体积,但是并不能减少构建的时间,甚至增加了构建的时间,因为压缩也是耗费资源和时间的。
npm i -D uglifyjs-webpack-plugin
// 代码压缩
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
//在configureWebpack中plugins加入
// 代码压缩
new UglifyJsPlugin({
uglifyOptions: {
output: {
comments: false // 去掉注释
},
//生产环境自动删除console
compress: {
drop_debugger: true,
drop_console: true, //注释console
pure_funcs: ["console.log"] // 移除console
}
},
sourceMap: false, //是否启用文件缓存
parallel: true //使用多进程并行运行来提高构建速度
}),
terser-webpack-plugin
webpack4 默认内置使用 terser-webpack-plugin
插件压缩优化代码,而该插件使用 terser
来缩小 JavaScript
。所以你可以不下载UglifyJsPlugin
直接使用 terser-webpack-plugin
const TerserPlugin = require('terser-webpack-plugin')
config
.when(process.env.NODE_ENV === 'development',
config => config.optimization.minimizer([new TerserPlugin({
terserOptions: {
mangle: true, // 混淆,默认也是开的,mangle也是可以配置很多选项的,具体看后面的链接
compress: {
drop_console: true,//传true就是干掉所有的console.*这些函数的调用.
drop_debugger: true, //干掉那些debugger;
pure_funcs: ['console.log'] // 如果你要干掉特定的函数比如console.info ,又想删掉后保留其参数中的副作用,那用pure_funcs来处理
}
}
})])
)
[terser-webpack-plugin] github.com/webpack-con…
gzip压缩
gzip可以减少构建打包后的体积,但是并不能减少构建的时间,甚至增加了构建的时间,因为压缩也是耗费资源和时间的。如果是使用nginx
进行发布的,记得nginx
配置支持gzip
npm install compression-webpack-plugin@6.1.1 --save-dev
const CompressionWebpackPlugin = require('compression-webpack-plugin');
//在configureWebpack中plugins加入
new CompressionWebpackPlugin({
// [file] 会被替换成原始资源。[path] 会被替换成原始资源的路径,[query] 会被替换成查询字符串
filename: '[path][base].gz',
// 压缩成gzip
algorithm: 'gzip',
// 使用正则给匹配到的文件做压缩,这里是给html、css、js以及字体做压缩
test: /.js$|.css$|.html$|.ttf$|.eot$|.woff$/,
// 只有大小大于该值的资源会被处理。单位是 bytes。默认值是 0。
threshold: 10240,
// 只有压缩率小于这个值的资源才会被处理。默认值是 0.8。
minRatio: 0.8
})
如果你按照上面优化一遍,启动时间直接接近翻倍。因为都是为了减少打包体积;
减少构建时间
HardSourceWebpackPlugin
HardSourceWebpackPlugin
是一个webpack插件,为模块提供中间缓存步骤。第一次构建将花费正常的时间。之后的建设将大大加快。就算重启,只要缓存还有效,都会使用。
npm install --save-dev hard-source-webpack-plugin
plugins: [
// 缓存 加速二次构建速度
new HardSourceWebpackPlugin({
//设置缓存目录的路径 相对路径或者绝对路径
// cacheDirectory是在高速缓存写入 ,设置缓存在磁盘中存放的路径
cacheDirectory: './../disk/.cache/hard-source/[confighash]',
// 也就是cacheDirectory中的[confighash]值
recordsPath: './../disk/.cache/hard-source/[confighash]/records.json',
//configHash在启动webpack实例时转换webpack配置,并用于cacheDirectory为不同的webpack配置构建不同的缓存
configHash: function (webpackConfig) {
// node-object-hash on npm can be used to build this.
return require('node-object-hash')({ sort: false }).hash(webpackConfig);
},
// Either false, a string, an object, or a project hashing function.
environmentHash: {
root: process.cwd(),
directories: [],
files: ['package-lock.json']
// files: ['./package-lock.json', './yarn.lock'],
},
//自动清除缓存
cachePrune: {
//缓存最长时间(默认2天),设置了30天
maxAge: 30 * 24 * 60 * 60 * 1000,
//所有的缓存大小超过size值将会被清除 (默认50MB) 设置了200MB
sizeThreshold: 200 * 1024 * 1024
},
}),
],
配置 externals 引入 cdn 资源
如果要用CDN引入一些资源,为了防止将某些 import 的包(package)打包到 bundle 中,而是在运行时(runtime)再去从外部获取这些扩展依赖,可以配置 externals 引入CDN资源;就是改变包引用路径,开发和生产环境统统改变有效。 比如 import Vue from 'vue' 默认来说 引用‘vue’默认从node_modules里面找vue,但是vue.config.js配置了externals,就不去引用node_modules,转而去找public/index.html里面的cdn有关vue的链接
vue.config.js
/**
* 生产环境上cdn,本地还是走node_modules,但是2者版本号保持一致,版本号看package.json
* https://unpkg.com/ unpkg如何找cdn链接
* cdn https://unpkg.com/jquery/ 留下最后一个/
* -> https://unpkg.com/browse/jquery@3.6.1/dist/jquery.slim.min.js 去掉browse/
* -> https://unpkg.com/https://unpkg.com/jquery@3.6.1/dist/jquery.slim.min.js
*/
const CDN = {
css: ["https://unpkg.com/vant@2.12.53/lib/index.css"],
js: [
"https://unpkg.com/vue@2.6.11/dist/vue.min.js",
"https://unpkg.com/vue-router@3.2.0/dist/vue-router.min.js",
"https://unpkg.com/vant@2.12.53/lib/vant.min.js",
"https://unpkg.com/axios@1.1.3/dist/axios.min.js",
"https://unpkg.com/vconsole@3.14.7/dist/vconsole.min.js",
"https://unpkg.com/moment@2.29.4/min/moment.min.js",
"https://unpkg.com/lodash@4.17.21/lodash.min.js",
],
};
module.exports = {
configureWebpack: (config) => {
if (process.env.NODE_ENV === "development") {
// ......
} else {
//生产环境
/**
* externals:externals配置的对象统统走cdn.(不管生产还是本地开发)
* 解释一下:import A from 'a' 去使用小a的时候不走node_modules了,统统走public/index.html里面配置的cdn链接。前提是要往html里面配置cdn各种链接.到这里就结束了 externals用法就完了
* 但是有的时候本地开发走node_modules,生产环境走cdn,于是需要区分环境
*/
config.externals = {
vue: "Vue", //解释一下:import XX from 'vue' 这里的'vue' 就是externals的key vue. "Vue"是cdn vue链接全局挂载window上的Vue
"vue-router": "VueRouter",
moment: "moment",
vant: "vant",
axios: "axios",
vconsole: "VConsole",
lodash: "_",
};
}
},
//修改已有插件用chainWebpack vuecli官网明确说过。
//这里就解释了为啥不在configureWebpack配置define,其实也可以配,不过很麻烦。
chainWebpack: (config) => {
config.plugin("define").tap((args) => {
//这样是搞不出来的 args[0]['process_env'].isProd=JSON.stringify(process.env.NODE_ENV !== "development");
args[0].isProd = JSON.stringify(process.env.NODE_ENV !== "development"); //都需要JSON.stringify
args[0].CDN = JSON.stringify(CDN);
return args;
});
},
};
public/index.html
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"
/>
<link rel="icon" href="<%= BASE_URL %>favicon.ico" />
<!-- 生产环境生效 配合externals一起用
直接就是isProd CDN使用 而不是htmlWebpackPlugin.options.isProd htmlWebpackPlugin.options.CDN使用.⚠️ -->
<% if(isProd){ %>
<% for(var i in CDN.css) {%>
<link rel="stylesheet" href="<%= CDN.css[i]%>">
<% } %>
<% } %>
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
<noscript>
<strong
>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work
properly without JavaScript enabled. Please enable it to
continue.</strong
>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
<!-- 生产环境生效 配合externals一起用-->
<% if(isProd){ %>
<% for(var i in CDN.js) {%>
<script src="<%= CDN.js[i]%>"> </script>
<% } %>
<% } %>
</body>
</html>
splitChunks分包
webpack 4 两个新的配置项optimization.splitChunks
和 optimization.runtimeChunk
,webpack通用模式现在已经做了一些通用性优化,适用于多数使用者。
常用参数:
minSize
(默认是30000):最小包体积,这里的单位是byte,超过这个大小的包会被splitChunks优化minChunks
(默认是1):模块的最小引用次数,如果引用次数低于这个值,将不会被优化maxInitialRequests
(默认是3):设置initial chunks的最大并行请求数maxAsyncRequests
(默认是5):按需加载时候最大的并行请求数,设置async chunks的最大并行请求数chunks
(默认是async) :initial、async和alltest
: 用于控制哪些模块被这个缓存组匹配到。原封不动传递出去的话,它默认会选择所有的模块。可以传递的值类型:RegExp、String和Functionname
(打包的chunks的名字):字符串或者函数(函数可以根据条件自定义名字)priority
:缓存组打包的先后优先级。
config
.optimization.splitChunks({
chunks: 'all',
cacheGroups: {
libs: {
name: 'chunk-libs',
test: /[\/]node_modules[\/]/,
priority: 10,
chunks: 'initial' // 仅打包最初依赖的第三方
},
elementUI: {
name: 'chunk-elementUI', // 将elementUI拆分为单个包
priority: 20, // 重量需要大于libs和app,否则将打包成libs或app
test: /[\/]node_modules[\/]_?element-ui(.*)/ // 为了适应cnpm
},
commons: {
name: 'chunk-commons',
test: resolve('src/components'), // 可以自定义规则
minChunks: 3, // 最小公共数
priority: 5,
reuseExistingChunk: true
}
}
})
config.optimization.runtimeChunk('single')
详情查看:www.webpackjs.com/plugins/spl…
其他操作
- 如果使用了
moment
这个包,可以使用dayjs
替代,或者如果没有国际化要求,可以只导入中文包 - 清理无用的依赖,可能存在安装了,却没有使用的依赖
- 清理工程垃圾文件,由于多人协助开发,可能存在一些无用又占内存的文件
- 启用webpack使用Vite构建工具
转载自:https://juejin.cn/post/7176165428115767353