Vue-cli3 webpack优化打包实践

5,678 阅读4分钟

前言

Vue-cli3提供了开箱即用的项目脚手架,自带的webpack配置已经经过了优化。但是当项目越来越庞大的时候,目前我的项目打个包都要三四分钟,并且首页加载也很慢。于是希望对webpack进行优化,进一步提升打包效率。

但是目前网上的各种资料,要么是针对webpack4之前版本的配置,要么是Vue-cli3默认配置已经包含了的内容。最后经过实践,本着宁缺毋滥的原则,我主要使用了以下插件:webpack-bundle-analyzer分析包大小、DllPlugin提前打包、gzip代码压缩。由于我的水平有限,有不正确的地方欢迎指正。

Vue-cli3已有的优化

首先,通过官方文档中的配置参考可以了解到一部分默认配置。通过 vue-inspect 命令,可以查看默认的webpack配置。例如babel-loader,已经配置了exclude,并且开启了cacheDirecorty,那么不用考虑优化loader了。

其次,从Vue-cli官方文档中,注意到parallel这个配置有如下说明。

是否为 Babel 或 TypeScript 使用 thread-loader。该选项在系统的 CPU 有多于一个内核时自动启用,仅作用于生产构建。

由此可见,网上很多教程中的多线程打包插件HappyPack也不需要了。

此外,webpack4中,modeproduction时会自动开启代码压缩Tree Shaking

进一步优化

既然Vue-cli3已经提供了以上这些优化,那么能做的也很有限了,于是我从提前打包开启gzip压缩两个方面来进行进一步优化。

提前打包

注意到打包后的项目中,有一个vendors.js文件体积特别大,有1.8M,而这个文件和引入的第三方库有关。我的项目引入了很多第三方库,例如ant-design-vueantvlodash等等,这些库文件一般是不会变动的,那么通过提前打包的方式,是不是就能缩短打包时间了?

接下来分析第三方库。由于我使用第三方库是按需引入的,不知道引入的哪些库比较占体积,这里就需要使用webpack-bundle-analyzer这个插件,它是通过矩形树图的方式,显示各个包中模块的大小和关系。通过插件的方式引入

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
    configureWebpack: config => {
        if (process.ENV.NODE_ENV === 'production') {
            config.plugins = [
                ...config.plugins,
                new BundleAnalyzerPlugin()
            ]
        }
    }
}

运行npm run build后,打开localhost:8888,下图就是我的项目结构

从图中看出,ant-design-vuemomentantvquill都是比较占体积的,可以考虑进行拆分打包,而lodash由于按需引入的原因,并没有占很大体积,所以不需要单独拆分。

在项目根目录创建webpack.dll.conf.js

const path = require('path')
const webpack = require('webpack')

// dll文件存放的目录
const dllPath = 'dll/'

const libs = {
    'ui': ['ant-design-vue'],
    'frame': ['vue', 'vuex', 'vue-router', 'moment', 'quill'],
    'chart': ['@antv/g2', '@antv/data-set']
}

module.exports = {
  entry: {
    // 需要提取的库文件
    ...libs
  },
  output: {
    path: path.join(__dirname, dllPath),
    filename: '[name].dll.js',
    // vendor.dll.js中暴露出的全局变量名
    // 保持与 webpack.DllPlugin 中名称一致
    library: '[name]_[hash]'
  },
  plugins: [
    // manifest.json 描述动态链接库包含了哪些内容
    new webpack.DllPlugin({
      path: path.join(__dirname, dllPath, '[name]-manifest.json'),
      // 保持与 output.library 中名称一致
      name: '[name]_[hash]',
      context: process.cwd()
    })
  ]
}

package.json中加入,并运行npm run dll

"scripts": {
    ...
    "dll": "webpack -p --progress --config ./webpack.dll.conf.js"
},`

vue.config.js中加入以下配置

config.plugins = [
    ...config.plugins,
    new webpack.DllReferencePlugin({
        context: process.cwd(),
        manifest: require('./dll/ui-manifest.json')
    }),
    new webpack.DllReferencePlugin({
        context: process.cwd(),
        manifest: require('./dll/frame-manifest.json')
    }),
    new webpack.DllReferencePlugin({
        context: process.cwd(),
        manifest: require('./dll/chart-manifest.json')
    })
]

打包完成了,但是webpack是不会帮我们自动引入这些单独打包的第三方库的,这里就需要用到add-asset-html-webpack-plugin这个插件。

const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin')

config.plugins = [
    ...config.plugins,
    new AddAssetHtmlPlugin({
        filepath: path.resolve(__dirname, './dll/*.js'), // dll文件位置
        publicPath: './js', // dll 引用路径
        outputPath: './js' // dll最终输出的目录
    })
]

开启gzip压缩

经过gzip压缩的包可以缩小约2/3体积,对网络传输的提升显著。开启gzip需要服务器的配合,以nginx为例,在nginx.conf中加入

server: {
    ...
    
    gzip on; # 是否开启gzip
    gzip_buffers 32 4K; #缓冲(压缩在内存中缓冲几块? 每块多大?)
    gzip_comp_level 6; #推荐6 压缩级别(级别越高,压的越小,越浪费CPU计算资源)
    gzip_min_length 100; #开始压缩的最小长度(再小就不要压缩了,意义不在)
    gzip_types application/javascript text/css text/xml; #对哪些类型的文件用压缩 如txt,xml,html ,css
    gzip_disable "MSIE [1-6]\."; #配置禁用gzip条件,支持正则。此处表示ie6及以下不启用gzip(因为ie低版本不支持)
    gzip_vary on; #是否传输gzip压缩标志
}

这样,客户端请求文件时,服务器就会自动压缩文件。但是服务器的压缩是需要CPU开销的,为了节省CPU资源,我们可以在打包的时候同时打包一份gzip,配置如下

const CompresssionPlugin = require('compression-webpack-plugin')

config.plugins = [
    ...config.plugins,
    new CompressionPlugin({
        test: /\.js$|\.html$|\.css$/,
        deleteOriginalAssets: false // 是否删除原文件
    })
]

结果

经过优化后,项目打包时间最短到过90s,但是有时候还是会超过三分钟,不确定是什么原因。 首页加载时间从8s缩短到5s,有了明显的提升。

感谢阅读,文中如有不正确,欢迎指正和探讨。