vlambda博客
学习文章列表

【补充】基于vue-cli创建的项目进行打包优化

一、前言

「阅读完该文章大概需要2.5min。」

  • 「读完该篇文章你能学到」

    1. vue-cli默认做了哪些优化?
    2. 在cli的基础上我们又能做哪些优化?
    3. vue.config.js中如何配置一些常用的 pluginloader
  • vue-cli的出现,让我们省掉了配置webpack的时间。也就是说,一个不懂webpack的人,也能直接上手开发。比如file-loader, url-loader会提前为我们配置好。

  • 「性能方面」,vue-cli也默认尽可能多的帮我们做了优化,比如cache-loader会在项目中预先做了配置。我们可以在控制台输入vue inspect > webpack.config.js,即可在webpack.config.js文件中查看cli预先定义好的基础配置。我们今天就在vue-cli搭建好的项目基础上聊一聊可优化的点。

二、项目源码

本文所用到的项目源码

三、量化指标

1. build时间

speed-measure-webpack-plugin插件可以在build的时候看到webpack的loader和plugin所用的时间,配置非常简单。如下:

// vue.config.js
module.exports = {
chainWebpackconfig => {
   config.plugin('speed')
   .use(SpeedMeasureWebpackPlugin)
 }
}

顺便看一下效果

2.build后包的大小以及包的多少

webpack-bundle-analyzer插件可以帮我们可视化的展示build时的每个包的大小以及依赖。vue-cli也帮我们做了默认的配置,我只需要在build的后面加一个参数--report即可。

// package.json
{
  "name""dll-vue",
  "version""0.1.0",
  "private"true,
  "scripts": {
    "serve""vue-cli-service serve",
    "build""vue-cli-service build",
    "report""vue-cli-service build --report",
  }
}

npm run report 之后,dist目录下就多了一个report.html文件,我们用浏览器打开这个文件看一下,右上角那个小蓝块是我们的vue代码,接下来我们主要来优化小蓝块之外的代码

【补充】基于vue-cli创建的项目进行打包优化
屏幕快照 2021-07-31 下午9.51.03.png

四、开始优化

1. include/exclude

我们通常配置include和exclude,来达到使loader仅仅处理匹配到的文件,像这样

// webpack.config.js
module.exports = {
  module: { 
      rules: [ 
          { 
              test/\.js[x]?$/
              use: ['babel-loader'], 
              include: [path.resolve(__dirname, 'src')] 
          }
         ] 
  },
}

而vue的transpileDependencies属性默认情况下 babel-loader 会忽略所有 node_modules 中的文件,其实已经满足了我们大部分需求。

2. resolve

resolve: 配置 webpack如何寻找模块所对应的文件,比如import * from 'xxx',xxx模块应该优先从node_modules中找,我们通过vue inspect > webpack.config.js导出的文件中在modules字段中可以清晰的看到已经将node_modules设置为优先寻找的模块

resolve: {
    // ...
    modules: [
      'node_modules',
      ...
    ]
    // ...
 }

3. happypack-plugin/thread-loader

一般来说,我们可以通过happack-plugin或者thread-loader开启多线程打包。vue-cli的parallel属性的含义是:是否为 Babel 或 TypeScript 使用 thread-loader,默认值为cpu的内核数,也就是说如果你系统是3核cpu,则build的时候,会自动在babel-loader和ts-loader执行时候开启3个线程。如果你想试着自己的配置一下,可以像下面这样。(不过我试着自己配置了之后,似乎没什么效果。也许是我配置的不对,欢迎大家来指正)

config.module.rule('vue')
          .use('thread-loader')
          .loader('thread-loader')
          .before('vue-loader')

4. noParse

如果一些第三方模块没有AMD/CommonJS规范版本,可以使用 noParse 来标识这个模块,这样 Webpack 会引入这些模块,但是不进行转化和解析,从而提升 Webpack 的构建性能 ,例如:jquery 、lodash,vue.config.js中可以这样配置:

// vue.config.js
module.exports = {
   //...
   configureWebpack:{
    module: {
      noParse: /^(lodash|moment)$/
    }
  }
  //...
}

5. ContextReplacementPlugin

一些依赖,我们也许只是用到了一部分,没必要全部解析,比如moment中的语言包,我们一般只用中文包就够了,所以可以这样配置:

// vue.config.js
module.exports = {
   //...
   configureWebpack:{
       plugins: [
           new webpack.ContextReplacementPlugin(/moment[/\\]locale$/, /zh-cn/)
       ]
  }
  //...
}

我们通过一张图片来体验一下, 左边为去掉语言包的效果

【补充】基于vue-cli创建的项目进行打包优化
moment.png

6. externals

一般来说像jQuery这些第三方的包,我们采用CDN的方式来引入,像这样

<-- public/index.html -->
<script src="https://code.jquery.com/jquery-3.6.0.min.> js"></script>

在使用的时候,我们希望通过import的方式引入,像这样

// App.vue\
import $ from 'jquery'
$('.today').text = 'today'

这就造成一个问题,build的时候就会将jquery再打包一次 我们可以配置externals来达到build时忽略掉指定的依赖

// vue.config.js
module.exports = {
   //...
   configureWebpack:{
    config.externals = {
      jquery'jQuery'
    }
  }
  //...
}

7. uglifyjs-webpack-plugin

在build的时候可以压缩代码的大小,有几个常用的配置可以单独提一下,比如去掉console.log,比如进行多进程压缩。可以这样:

// vue.config.js
module.exports = {
   //...
   configureWebpack:{
    config.optimization.minimizer = [
      new UglifyJsPlugin({
       uglifyOptions: {
        compress: {
         drop_consoletrue// 去掉console
        }
        paralleltrue//默认并发运行数:os.cpus().length - 1
       }
      })
     ]
  }
  //...
}

8. compression-webpack-plugin

我们可以将代码压缩为.gz文件,浏览器也是可以识别的。可以这样配置

//vue.config.js
module.exports = {
   //...
   configureWebpack:{
    plugins: [
        new CompressionWebpackPlugin()
    ]
  }
  //...
}

9. DllPlugin 和 DllReferencePlugin

  • 对于变化几率很小的一些第三方包,其实没必要build的时候都要打包一次, 可以把这些第三方包单独抽离出来,提前打包好。
  • webpack本身是要体现出模块间的依赖关系,当我们将一些包抽离出来后,维护之前的依赖关系就需要 manifest.json这个文件。让我们从接下来的实战中来学习它。

<1> 新建一个配置文件webpack.dll.config.js

// webpack.dll.config.js
module.exports = {
  mode'production',
  entry: {
    vue_vendor: ['vue/dist/vue.runtime.esm.js''vuex',  'vue-router''element-ui'],
    other_vendor: ['lodash''moment']
  },
  output: {
    filename'[name].dll.js',
    path: path.resolve(__dirname, './public/dll'),
    library'[name]_[hash]'
  },\
  plugins: [
    new webpack.DllPlugin({
      name'[name]_[hash]',
      path: path.resolve(__dirname, '[name].manifest.json')
    })
  ]
}

<2> 为了方便,我们将读取该配置文件的命令写到package.json文件中,像这样。(需要安装webpack-cli)

// package.json
{
  .....
  "scripts": {
    .....
    "dll""webpack --config ./webpack.dll.config.js"
  }
  .....
}

<3> 执行npm run dll后可以看到生成两个manifest文件,像这样

【补充】基于vue-cli创建的项目进行打包优化
屏幕快照 2021-08-02 下午8.06.25.png

<4> 修改vue.config.js,引入依赖文件,并自动将dll下的文件插入到index.html中(这里我们引入一个插件add-asset-html-webpack-plugin

module.exports = {
  chainWebpackconfig => {
    // 多个manifest.json文件就需要写多次
    config.plugin('vendorDll1')
    .use(webpack.DllReferencePlugin, [
      {
        context: __dirname,
        manifestrequire('./public/manifest/other_vendor.manifest.json')
      }
    ])

    config.plugin('vendorDll2')
    .use(webpack.DllReferencePlugin, [
      {
        context: __dirname,
        manifestrequire('./public/manifest/vue_vendor.manifest.json')
      }
    ])

    // 将dll下的文件自动插入到index.html中
    config.plugin('asset')
    .use(AddAssetHtmlWebpackPlugin, [
      [
        {
          filepath: path.resolve(__dirname, 'public/dll/vue_vendor.dll.js'),
          outputPath'dll',
          publicPath'/dll'
        },
        {
          filepath: path.resolve(__dirname, 'public/dll/other_vendor.dll.js'),
          outputPath'dll',
          publicPath'/dll'
        }
      ]
    ])
  }
}

10. optimization.splitChunks

抽离公共代码,通过配置splitChunks可抽离公共的代码,防止重复,我没有在自己的项目中用,

//vue.config.js
module.exports = {
   //...
   chainWebpackconfig => {
      config.optimization.splitChunks({
          chunks'all',
          cacheGroups: {}
      })
   }
  //...
}

五、效果展示

【补充】基于vue-cli创建的项目进行打包优化
section1.png
【补充】基于vue-cli创建的项目进行打包优化
section2.png
all.png

六、补充几个不需要配置的优化点

1. extensions

当我们导入模块时,假如没有指定后缀,期望优先匹配的文件格式,我们直接看vue-cli默认的配置extensions: ['.mjs', '.js', '.jsx', '.vue', '.json', '.wasm']

2. tree-shaking

我们通过import方式引入,webpack会自动移除掉没有用到的模块代码

3. Scope hosting

作用域提升:比如

let a = 1
let b = 2
let c = a + b

// webpack自动优化为
c = 3

4. 路由懒加载

通过() => import('xxx')方式引入,可达到路由懒加载的效果

六、总结

  • 「该文章为最近优化团队项目的webpack打包速度,做的总结。」

  • 「如果有不对的地方欢迎大家在讨论区纠正」

  • 「如果觉得有用,欢迎点个赞啦」