一转眼已经是2019年了,短短三四年时间,webpack打包工具已经成为后端开发必备工具。 面试题都讲完了,后端页面优化的方法有哪些? 大家可以轻松地谈论缓存、压缩文件、CSS精灵图像、CDN部署等各种技术,但昨晚有所不同。 也许你去笔试会问,你知道webpack的打包原理吗,webpack的打包优化方法有哪些? 那我该怎么说呢? 作者无意研究webpack的打包优化。 也许你读过类似的优化文章~但作者还是希望能给你一些新的启发~
1. 准备工作:测速与分析包
既然我们要优化webpack打包,那么我们就必须提前分析我们的bundle文件,分析每个模块的大小,分析打包时间的时长。 webpack插件主要有两个,speed-measure-webpack-plugin和webpack-bundle-analyzer,前者用于测速,后者用于分析bundle文件。
具体配置
const SpeedMeasurePlugin = require("speed-measure-webpack-plugin");
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
const smp = new SpeedMeasurePlugin({
outputFormat:"human",
});
module.exports = {
configureWebpack: smp.wrap({
plugins: [
new webpack.ProvidePlugin({
$: "zepto",
Zepto: "zepto",
}),
new BundleAnalyzerPlugin(),
],
optimization: {
splitChunks: {
cacheGroups: {
echarts: {
name: "chunk-echarts",
test: /[\/]node_modules[\/]echarts[\/]/,
chunks: "all",
priority: 10,
reuseExistingChunk: true,
enforce: true,
},
demo: {
name: "chunk-demo",
test: /[\/]src[\/]views[\/]demo[\/]/,
chunks: "all",
priority: 20,
reuseExistingChunk: true,
enforce: true,
},
page: {
name: "chunk-page",
test: /[\/]src[\/]/,
chunks: "all",
priority: 10,
reuseExistingChunk: true,
enforce: true,
},
vendors: {
name: "chunk-vendors",
test: /[\/]node_modules[\/]/,
chunks: "all",
priority: 5,
reuseExistingChunk: true,
enforce: true,
},
},
},
},
})
}
因为是基于vue-cli脚手架,虽然vue-cli已经帮你做了一些优化工作,但是可以看出原项目初始配置设置了splitchunk进行代码分割,这在小项目中是非常有必要的,毕竟你不希望你的用户阻塞加载一个5MB的JS文件,所以代码分割和延迟加载是必要的。
扯远了,我们来看看这个配置。 您需要使用 smp 重新打包配置,因为 SpeedMeasurePlugin 将为您的其他 Plugin 对象包装一层代理。 这样做的目的是为了知道插件什么时候开始和结束~
其次,BundleAnalyzerPlugin 就像普通插件一样,只需将其加载到插件链接列表旁边即可。
接下来我们看一下初始打包时间和打包内容分析:
可以看到项目中三个较大的包,其中两个是我们的第三方依赖, Three.js、lottie、lodash、echarts等。
2.开始逐步优化
2.1 缩小文件搜索和处理的范围
这是webpack优化中的常规操作。 基本上是优化模块和文件搜索,减少加载器对一些不必要模块的处理。 但是 vue-cli 中的 loader 并没有暴露给我们操作,所以它的外部 loader 我们很难优化处理,但毕竟 vue-cli 中的配置项已经优化了 loader 的搜索路径。 如果你的项目也使用了vue-cli,你可以通过下面的命令行查看你现有的配置文件怎么样:
npx vue-cli-service inspect > output.js
具体可以参考vuecli官方文档。
resolve:{
modules: [path.resolve(__dirname, 'node_modules')],
alias:{
'three':path.resolve(__dirname, './node_modules/three/build/three.min.js'),
'zepto$':path.resolve(__dirname, './node_modules/zepto/dist/zepto.min.js'),
'swiper$':path.resolve(__dirname, './node_modules/swiper/dist/js/swiper.min.js'),
'lottie-web$':path.resolve(__dirname, './node_modules/lottie-web/build/player/lottie.min.js'),
'lodash$':path.resolve(__dirname, './node_modules/lodash/lodash.min.js'),
}
},
module:{
noParse:/^(vue|vue-router|vuex|vuex-router-sync|three|zepto|swiper|lottie-web|lodash)$/
},
优化疗效:2s?
可以看到时间减少了两三秒,在30s上下波动,并没有太大的差别。
2.2 尝试使用happypack
由于在webpack优化之前看了很多关于webpack优化的文章,所以我也想尝试使用happypack来优化打包时间。 想要用happypack打包之前,大致有两种说法:
1、webpack4中已经默认采用多线程打包,所以happypack打包效果并不显着;
2、vue不支持happypack打包,需要设置thread-loader。
不过笔者想了想,还是试一试吧,大不了我只为JS和CSS文件设置happypack。
但是问题又来了,vue-cli外部封装了loader,这个时候怎么获取它的配置,并重写上面的loader配置。
通过阅读vue-cli的官方文档,我们可以看到如下介绍:
配置Webpack类型:对象| 作用 如果这个值是一个对象,它会被 webpack-merge 合并到最终的配置中。 如果该值是一个函数,它将接收解析的配置作为参数。 此函数可以更改配置并且不返回任何内容,也可以返回配置的克隆或合并版本。
为此,笔者专门调试进入vue-cli的源码发现:
流程介绍:
由于我们执行命令行vue-cli-service构建,实际上是去node_modules的.bin文件夹中查找对应的可执行文件,.bin下的vue-cli-service会映射到对应的第三个中执行-方库文档。
所以我们可以找到这个可执行文件的地址:
/node_modules/@vue/cli-service/bin/vue-cli-service.js
找到入口后,我们要步入nodejs的调试。 往年的开发中,我们都会以node --inspect app.js的形式启动一个后台服务,然后在Google Chrome中进入调试界面(F12选择红色小按钮)
但是这里有一个困难,因为我们的包创建是执行一次的,这与后台服务不同webpack优化vue,后台服务是实时点击的,服务仍然在运行。 看看吧,如果你想调试普通的nodejs文件,需要使用这个方法:
node --inspect-brk=9229 app.js
因此,为了强制进入vue-cli的源码进行调试,我们可以看到vue-cli的处理流程,我们需要像这样输入以下命令行:
node --inspect-brk=9229 node_modules/@vue/cli-service/bin/vue-cli-service.js build
上面的命令行相当于vue-cli-service build。
通过这种方法,我们最终进入了vue-cli的源码,看到它的执行过程后,就可以在相应的位置设置断点来查看此时作用域内的变量数据了。
可以看到vue-cli源码中的这一段操作会执行我们传入的函数,并判断函数是否有返回值来决定是否合并到其内部配置config中。
通过这段代码我们可以看到,如果我们将configWepack配置为函数,然后通过参数获取config配置项,它本身就是一个对象,而对象是一种保留引用的方式,所以如果我们直接修改传入的config object 改变一下,就可以达到我们原来的目的了! 修改vue-cli的外部加载器!
当然,除了单步进入断点查看配置之外,正如我刚才所说,我们还可以通过命令行输出作为输出文件来查看现有的配置。
这里可以截图看看vue-cli的内部配置:
可能有点废话了,但是通过断点的形式我们可以看到,虽然vue-cli已经对js文件设置了exclusion,但是它也帮我们设置了cache-loader,也就是说常规的优化方式之一webpack的就是使用cache-loader也为我们缓存了它。
回到最初的起点,我们要处理的是JS和CSS的加载器,所以模仿大部分配置,我做了以下修改:
configureWebpack:(config)=>{
console.log("webpack config start");
let originCssRuleLoader = config.module.rules[6].oneOf[0].use;
let newCssRuleLoader = 'happypack/loader?id=css';
config.module.rules[6].oneOf[0].use = newCssRuleLoader
config.module.rules[6].oneOf[1].use = newCssRuleLoader
config.module.rules[6].oneOf[2].use = newCssRuleLoader
config.module.rules[6].oneOf[3].use = newCssRuleLoader
...//other code
}
尝试更改 css 的加载器配置。 然后配置插件:
plugins: [
new HappyPack({
id: 'css',
threads: 4,
loaders: originCssRuleLoader
}),
],
我以为这样就可以了,但是很遗憾地告诉你,报错了……
可以看到错误的内容是处理vue文件时出现错误。
怎么解决
作者有百度和微软。 这可能意味着happypack不支持vue-loader。 同时我也根据错误报告检查了解决方案。 通过设置并行参数,仍然无效。
作者甚至怀疑我的happypack配置错误,于是我将原来的配置移植到另一个非vue项目中webpack优化vue,一切正常。 答:这个问题没有答案~
原因分析:
由于vue文件中会含有丰富的CSS,因此vue-loader会提取CSS并将其交给其他加载器进行处理。 vue-loader-plugin 会通过在 vue 文件前面添加查询字符串来告诉其他加载器,以处理该文件。 这是什么意思? 当我们的vue-loader处理该文件时,它会通知其他loader去处理,但是此时loader配置已经被我们重写为happypack,而vue与happypack不兼容,最终导致错误。 很遗憾地告诉大家,vue-cli访问happypack失败。
(注:这部分主要是作者在webpack优化过程中的探索,虽然最终我们不能让自己的webpack包得到很好的优化,但是在探索的过程中,我们也可以学到很多东西~包括vue -cli 配置对象处理?如何调试普通文件nodejs代码?vue-loader中vue文件处理流程?vue-loader-plugin帮我们做什么?而这种东西需要自己慢慢看,踩着坑慢慢理解的~)
2.3 使用dll插件
和大多数webpack优化教程一样,作者也尝试借助dllplugin进行优化。 这个插件的本质就是把我们常用的第三方模块提取出来,单独打包,然后插入到我们的html页面中。 以后我们每次打包的时候,就不需要和第三方模块打交道了。 毕竟第三方模块往往有几万行。
流程介绍:
1.配置webpack.dll.js为第三方库打包
2.在vue.config.js中配置插件
3、将dll封装的js文件引入html中。 (通常采用部署CDN的形式)
由于项目中有很多小的第三方库,比如三、echart等,所以作者做了如下配置:(webpack.dll.js)
const webpack = require("webpack")
const path = require("path")
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: {
vuebundle: [
'vue',
'vue-router',
'vuex',
],
utils:[
'lodash',
'swiper',
'lottie-web',
'three',
],
echarts:[
'echarts/lib/echarts',
"echarts/lib/chart/bar",
"echarts/lib/chart/line",
"echarts/lib/component/tooltip",
"echarts/lib/component/title",
"echarts/lib/component/legend",
]
},
output: {
path: path.resolve(__dirname, './static/'),
filename: '[name].dll.js',
library: '[name]_library'
},
plugins: [
new webpack.DllPlugin({
path: path.join(__dirname, 'build', '[name]-manifest.json'),
name: '[name]_library'
})
]
}
定义不同库的大小,打包了三个包,为什么不打包成一个包呢? 包太大,你不希望你的用户加载一个小的JS文件包并阻塞它,影响页面性能。
接下来是vue.config.js的配置:
plugins: [
new webpack.ProvidePlugin({
$: "zepto",
Zepto: "zepto",
}),
new DllReferencePlugin({
manifest: require('./build/echarts-manifest.json'),
}),
new DllReferencePlugin({
manifest: require('./build/utils-manifest.json'),
}),
new DllReferencePlugin({
manifest: require('./build/vuebundle-manifest.json'),
}),
new BundleAnalyzerPlugin(),
]
引入了DllPlugin。 接下来配置 HTML:
(因为作者没有将DLL打包的js文件上传到CDN,所以只能在本地搭建一个node服务器来返回静态资源)
然后npm runserve,开始页面调试开发~
舒服~
优化结果:
由于缺少小型第三方库,时间控制在20s左右。 优化还是比较显着的~
3.优化与探索总结
优化到这里,基本上就结束了。
webpack常见的优化方法有优化路径搜索、设置缓存、happypack、dllplugin等。 前两个vue-cli已经帮我们做了一些,但是happypack因为与vue不兼容而无法访问。 dllplugin 提取第三方库进行了显着优化。 当然,作者也尝试过消除一些项目中无用的代码,但并不痛苦。
webpack优化方法总结:
1.优化模块搜索路径
2. 消除不必要和无用的模块
3.设置缓存:缓存加载器的执行结果(cacheDirectory/cache-loader)
4.设置多线程:HappyPack/thread-loader
5.dllplugin提取第三方库
当然这是针对开发的优化,如果是针对部署的优化呢? 我们可以设置splitchunk、按需加载、部署CDN等,这里不再展开。
终于
希望这篇文章能让你有所收获~webpack已经是前端小伙子必备的技能了~如果有时间,可以深入研究一下webpack的配置和原理,你也会有所收获的! 感谢您的观看~
发表评论