如今,webpack 毫无疑问是前端构建领域里最耀眼的一颗星,无论你前端走哪条路线,都需要有很强的webpack 知识。webpack 的基本用法这里就不展开讲了。主要探讨一下如何提高 webpack 的打包速度。
这篇文章以 vue cli3.0+,webpack4.0+,nodejs10.0+ 这几个版本为例。
一、打包分析
1.1、速度分析
我们的目的是优化打包速度,那肯定需要一个速度分析插件,此时 speed-measure-webpack-plugin 就派上用场了。它的作用如下:
分析整个打包总耗时
每个 plugin 和 loader 的耗时情况
安装插件
npm i -D speed-measure-webpack-plugin
然后修改 vue.config.js 配置文件 (vuecli3+ 版本的配置文件统一在这个文件里修改,如果没有该文件,在根目录新建一个)
// 导入速度分析插件constSpeedMeasurePlugin=require("speed-measure-webpack-plugin");// 实例化插件const smp =newSpeedMeasurePlugin();module.exports={
configureWebpack: smp.wrap({
plugins:[// 这里是自己项目里需要使用到的其他插件newyourOtherPlugin()]})}
运行打包命令之后,可以看到,打包总耗时为 2min,51.99s
1.2、体积分析
分析完打包速度之后,接着我们来分析打包之后每个文件以及每个模块对应的体积大小。使用到的插件为 webpack-bundle-analyzer,构建完成后会在 8888 端口展示大小。
首先,安装插件
npm i -D webpack-bundle-analyzer
修改 vue.config.js 配置文件
// 导入速度分析插件const SpeedMeasurePlugin =require("speed-measure-webpack-plugin");// 导入体积分析插件const BundleAnalyzerPlugin =require("webpack-bundle-analyzer").BundleAnalyzerPlugin;// 实例化速度分析插件const smp =newSpeedMeasurePlugin();
module.exports ={
configureWebpack: smp.wrap({
plugins:[// 实例化体积分析插件newBundleAnalyzerPlugin()]})}
构建之后可以看到,其中黄色块 chunk-vendors 文件占比最大,为 1.34MB
二、打包优化
2.1、多进程多实例构建,资源并行解析
多进程构建的方案比较知名的有以下三个:
thread-loader (推荐使用这个)
parallel-webpack
HappyPack
这里以 thread-loader 为例配置多进程多实例构建
安装 loader
npm i -D thread-loader
接下来在 vue.config.js 配置文件中使用该 loader
// 导入速度分析插件const SpeedMeasurePlugin =require("speed-measure-webpack-plugin");// 实例化插件const smp =newSpeedMeasurePlugin();module.exports ={
configureWebpack: smp.wrap({module:{
rules:[{
test:/\.js$/,
use:['thread-loader']}]}})}
然后看下构建花费的时间, 2min,49.21s,相较于之前提升了 5s
2.2、公用代码提取,使用 CDN 加载
对于vue,vuex,vue-router,axios,echarts,swiper等我们可以利用webpack的externals参数来配置,这里我们设定只需要在生产环境中才需要使用。
下面配置 vue.config.js
// 导入速度分析插件const SpeedMeasurePlugin =require("speed-measure-webpack-plugin");// 导入体积分析插件const BundleAnalyzerPlugin =require("webpack-bundle-analyzer").BundleAnalyzerPlugin;//判断是否为生产环境const isProduction = process.env.NODE_ENV==='production';//定义 CDN 路径,这里采用 bootstrap 的 cdnconst cdn ={
css:['https://cdn.bootcss.com/Swiper/4.5.1/css/swiper.min.css'],
js:['https://cdn.bootcss.com/vue/2.6.10/vue.min.js','https://cdn.bootcss.com/vue-router/3.1.3/vue-router.min.js','https://cdn.bootcss.com/vuex/3.1.1/vuex.min.js','https://cdn.bootcss.com/axios/0.19.0/axios.min.js','https://cdn.bootcss.com/echarts/4.3.0/echarts.min.js','https://cdn.bootcss.com/Swiper/4.5.1/js/swiper.min.js',]}// 实例化插件const smp =newSpeedMeasurePlugin();module.exports ={chainWebpack:config=>{// 生产环境配置if(isProduction){// 生产环境注入 cdn
config.plugin('html').tap(args=>{
args[0].cdn = cdn;return args;});}},
configureWebpack: smp.wrap({module:{
rules:[{
test:/\.js$/,
use:['thread-loader']}]},
plugins:[newBundleAnalyzerPlugin()],//生产环境注入 cdn
externals: isProduction &&{'vue':'Vue','vuex':'Vuex','vue-router':'VueRouter','axios':'axios','echarts':'echarts','swiper':'Swiper'}||{}})}
紧接着,改造 html 页面。用于让我们配置的 cdn 路径注入到 html 页面
<!DOCTYPE html><htmllang="zh"><head><metacharset="utf-8"><metahttp-equiv="X-UA-Compatible"content="IE=edge"><metaname="viewport"content="width=device-width,initial-scale=1.0"><!-- 使用 CDN 的 CSS 文件 -->
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.css) { %>
<linkhref="<%= htmlWebpackPlugin.options.cdn.css[i] %>"rel="stylesheet">
<% } %>
</head><body><noscript><strong>We're sorry but eye-admin doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><divid="app"></div><!-- built files will be auto injected --><!-- 使用 CDN 的 JS 文件 -->
<% for (var i in htmlWebpackPlugin.options.cdn && htmlWebpackPlugin.options.cdn.js) { %>
<scriptsrc="<%= htmlWebpackPlugin.options.cdn.js[i] %>"></script>
<% } %>
</body></html>
最重要的一步,看下效果,可以看到现在耗时 1min,39.19s,整整提升了 1 分多钟!
2.3、多进程多实例并行压缩
并行压缩主流有以下三种方案
使用 parallel-uglify-plugin 插件
uglifyjs-webpack-plugin 开启 parallel 参数
terser-webpack-plugin 开启 parallel 参数 (推荐使用这个,支持 ES6 语法压缩)
安装插件依赖
npm i -D terser-webpack-plugin
接下来在 vue.config.js 配置文件中使用插件,最终的配置文件如下
// 导入速度分析插件const SpeedMeasurePlugin =require("speed-measure-webpack-plugin");// 导入代码压缩插件const TerserPlugin =require("terser-webpack-plugin");// 导入体积分析插件const BundleAnalyzerPlugin =require("webpack-bundle-analyzer").BundleAnalyzerPlugin;//判断是否为生产环境const isProduction = process.env.NODE_ENV==='production';//定义 CDN 路径,这里采用 bootstrap 的 cdnconst cdn ={
css:['https://cdn.bootcss.com/Swiper/4.5.1/css/swiper.min.css'],
js:['https://cdn.bootcss.com/vue/2.6.10/vue.min.js','https://cdn.bootcss.com/vue-router/3.1.3/vue-router.min.js','https://cdn.bootcss.com/vuex/3.1.1/vuex.min.js','https://cdn.bootcss.com/axios/0.19.0/axios.min.js','https://cdn.bootcss.com/echarts/4.3.0/echarts.min.js','https://cdn.bootcss.com/Swiper/4.5.1/js/swiper.min.js',]}// 实例化插件const smp =newSpeedMeasurePlugin();module.exports ={chainWebpack:config=>{// 生产环境配置if(isProduction){// 生产环境注入 cdn
config.plugin('html').tap(args=>{
args[0].cdn = cdn;return args;});}},
configureWebpack: smp.wrap({module:{
rules:[{
test:/\.js$/,
use:['thread-loader']}]},
plugins:[newBundleAnalyzerPlugin()],//生产环境注入 cdn
externals: isProduction &&{'vue':'Vue','vuex':'Vuex','vue-router':'VueRouter','axios':'axios','echarts':'echarts','swiper':'Swiper'}||{},
optimization:{
minimizer:[newTerserPlugin({
parallel:4})]}})}
2.4、使用 polyfill 动态服务
动态 polyfill 指的是根据不同的浏览器,动态载入需要的 polyfill。 Polyfill.io 通过尝试使用 polyfill 重新创建缺少的功能,可以更轻松地支持不同的浏览器,并且可以大幅度的减少构建体积。
Polyfill Service 原理
识别 User Agent,下发不同的 Polyfill
使用方法:在 index.html 中引入如下 script 标签
<scriptcrossorigin="anonymous"src="https://polyfill.io/v3/polyfill.min.js"></script