【實戰(zhàn)篇】基于vue-cli4創(chuàng)建的項目進行打包優(yōu)化

一、前言

閱讀完該文章大概需要2.5min。

  • 讀完該篇文章你能學到

    1. vue-cli默認做了哪些優(yōu)化?
    2. 在cli的基礎上我們又能做哪些優(yōu)化?
    3. vue.config.js中如何配置一些常用的pluginloader
  • vue-cli的出現(xiàn),讓我們省掉了配置webpack的時間。也就是說,一個不懂webpack的人,也能直接上手開發(fā)。比如file-loader, url-loader會提前為我們配置好。

  • 性能方面vue-cli也默認盡可能多的幫我們做了優(yōu)化,比如cache-loader會在項目中預先做了配置。我們可以在控制臺輸入vue inspect > webpack.config.js,即可在webpack.config.js文件中查看cli預先定義好的基礎配置。我們今天就在vue-cli搭建好的項目基礎上聊一聊可優(yōu)化的點。

二、項目源碼

本文所用到的項目源碼

三、量化指標

1. build時間

speed-measure-webpack-plugin插件可以在build的時候看到webpack的loader和plugin所用的時間,配置非常簡單。如下:

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

順便看一下效果


屏幕快照 2021-07-31 下午4.57.58.png

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

webpack-bundle-analyzer插件可以幫我們可視化的展示build時的每個包的大小以及依賴。vue-cli也幫我們做了默認的配置,我只需要在build的后面加一個參數(shù)--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代碼,接下來我們主要來優(yōu)化小藍塊之外的代碼

屏幕快照 2021-07-31 下午9.51.03.png

四、開始優(yōu)化

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 中的文件,其實已經(jīng)滿足了我們大部分需求。

2. resolve

resolve: 配置 webpack如何尋找模塊所對應的文件,比如import * from 'xxx',xxx模塊應該優(yōu)先從node_modules中找,我們通過vue inspect > webpack.config.js導出的文件中在modules字段中可以清晰的看到已經(jīng)將node_modules設置為優(yōu)先尋找的模塊

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

3. happypack-plugin/thread-loader

一般來說,我們可以通過happack-plugin或者thread-loader開啟多線程打包。vue-cli的parallel屬性的含義是:是否為 Babel 或 TypeScript 使用 thread-loader,默認值為cpu的內核數(shù),也就是說如果你系統(tǒng)是3核cpu,則build的時候,會自動在babel-loader和ts-loader執(zhí)行時候開啟3個線程。如果你想試著自己的配置一下,可以像下面這樣。(不過我試著自己配置了之后,似乎沒什么效果。也許是我配置的不對,歡迎大家來指正)

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

4. noParse

如果一些第三方模塊沒有AMD/CommonJS規(guī)范版本,可以使用 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/)
       ]
  }
  //...
}

我們通過一張圖片來體驗一下, 左邊為去掉語言包的效果

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_console: true, // 去掉console
        }
        parallel: true, //默認并發(fā)運行數(shù):os.cpus().length - 1
       }
      })
     ]
  }
  //...
}

8. compression-webpack-plugin

我們可以將代碼壓縮為.gz文件,瀏覽器也是可以識別的??梢赃@樣配置

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

9. DllPlugin 和 DllReferencePlugin

  • 對于變化幾率很小的一些第三方包,其實沒必要build的時候都要打包一次, 可以把這些第三方包單獨抽離出來,提前打包好。
  • webpack本身是要體現(xiàn)出模塊間的依賴關系,當我們將一些包抽離出來后,維護之前的依賴關系就需要manifest.json這個文件。讓我們從接下來的實戰(zhàn)中來學習它。

<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> 執(zhí)行npm run dll后可以看到生成兩個manifest文件,像這樣

屏幕快照 2021-08-02 下午8.06.25.png

<4> 修改vue.config.js,引入依賴文件,并自動將dll下的文件插入到index.html中(這里我們引入一個插件add-asset-html-webpack-plugin

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

    config.plugin('vendorDll2')
    .use(webpack.DllReferencePlugin, [
      {
        context: __dirname,
        manifest: require('./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 = {
   //...
   chainWebpack: config => {
      config.optimization.splitChunks({
          chunks: 'all',
          cacheGroups: {}
      })
   }
  //...
}

五、效果展示

section1.png
section2.png
all.png

六、補充幾個不需要配置的優(yōu)化點

1. extensions

當我們導入模塊時,假如沒有指定后綴,期望優(yōu)先匹配的文件格式,我們直接看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自動優(yōu)化為
c = 3

4. 路由懶加載

通過() => import('xxx')方式引入,可達到路由懶加載的效果

六、總結

  • 該文章為最近優(yōu)化團隊項目的webpack打包速度,做的總結。

  • 如果有不對的地方歡迎大家在討論區(qū)糾正

  • 歡迎關注公眾號:前端小卡

  • 如果覺得有用,歡迎點個贊啦

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容