1. 程式人生 > 實用技巧 >利用DllPlugin效能優化

利用DllPlugin效能優化

介紹

在用 Webpack 打包的時候,對於一些不經常更新的第三方庫,比如 react,lodash,vue 我們希望能和自己的程式碼分離開,Webpack 社群有兩種方案
CommonsChunkPlugin
DLLPlugin
對於 CommonsChunkPlugin,webpack 每次打包實際還是需要去處理這些第三方庫,只是打包完之後,能把第三方庫和我們自己的程式碼分開。而 DLLPlugin 則是能把第三方程式碼完全分離開,即每次只打包專案自身的程式碼。Dll這個概念是借鑑了Windows系統的dll,一個dll包,就是一個純純的依賴庫,它本身不能執行,是用來給你的app引用的

使用

新增配置檔案

要使用 DLLPlugin,需要額外新建一個配置檔案webpack.dll.js,該配置檔案用來打包生成第三方庫。
webpack.dll.js

const webpack = require('webpack')
const path = require('path')
module.exports = {
  mode: 'production',
  entry: {
    vendor: ['lodash'],
    react: ['react']
  },
  output: {
    filename: '[name].dll.js',
    path: path.resolve(__dirname, './dll'),
    library: '[name]',
    // libraryTarget: 'umd' // 將入口檔案的返回值以什麼型別的方式匯出,umd比較全,包括amd、cmd、window
  },
  plugins: [
    new webpack.DllPlugin({
      name: '[name]',
      path: path.join(__dirname, './dll/[name].manifest.json')
    })
  ]
}

然後執行 命令列執行"webpack --config webpack-dll.js"
生成打包後的檔案

接下來就是,這麼在業務程式碼裡使用打包好的第三方模組的事情了。

修改配置檔案webpack-common.js

關鍵程式碼如下:

const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');
....
  plugins: [
    new AddAssetHtmlWebpackPlugin({//將打包好的dll檔案掛載到html中
      filepath: path.resolve(__dirname, './dll/react.dll.js')
    }),
    new AddAssetHtmlWebpackPlugin({
      filepath: path.resolve(__dirname, './dll/vendor.dll.js')
    }),
    new webpack.DllReferencePlugin({
      manifest: path.resolve(__dirname, './dll/vendor-manifest.json')
    }),
    new webpack.DllReferencePlugin({
      manifest: path.resolve(__dirname, './dll/react-manifest.json')
    })
  ],

瀏覽器控制檯,可以看到第三方模組,已經作為全域性變數,引入到了入口檔案中。

但是每次在webpack.dll.js新加第三方模組,都要在webpack-common.js裡新增DllReferencePlugin等,比較麻煩,我們可以用node寫一個檔案迴圈,自己讀取檔案數量,自動修改plugin
關鍵程式碼如下:

const fs = require('fs')
const files = fs.readdirSync(path.resolve(__dirname, '../dll'));
const plugins = [
  new HtmlWebpackPlugin({
    template: './src/index.html'
  }),
  new CleanWebpackPlugin(),
  new BundleAnalyzerPlugin(),
  new webpack.ProvidePlugin({
    _: 'lodash'
  }),
]
files.forEach(file => {
  if (/.*\.dll.js/.test(file)) {
    plugins.push(new AddAssetHtmlWebpackPlugin({//將打包好的dll檔案掛載到html中
      filepath: path.resolve(__dirname, '../dll', file)
    }))
  }
  if (/.*\.manifest.json/.test(file)) {
    plugins.push(new webpack.DllReferencePlugin({//分析第三方模組是否已經在dll檔案裡,如果裡面有就不用再node_modules在分析打包了
      manifest: path.resolve(__dirname, '../dll', file)
    }))
  }
})

總結整體流程

通過dllPlugin生成manifets.json和vendor.js,vendor.js會自執行返回一個載入函式vendor(名字可配置),通過閉包將模組儲存在記憶體中,注意vendor是一個全域性變數。
webpack通過DllReferencePlugin在打包的時候分析業務程式碼中使用了哪些第三方模組,哪些模組是不需要打包進業務程式碼中,而是去vendor.js中獲取。
vendor中獲取的模組是通過呼叫全域性函式vendor(id)來進行引入。

補充

CommonsChunkPlugin 外掛每次打包的時候還是會去處理一些第三方依賴庫,只是它能把第三方庫檔案和我們的程式碼分開掉,生成一個獨立的js檔案。但是它還是不能提高打包的速度。

DLLPlugin 它能把第三方庫程式碼分離開,並且每次檔案更改的時候,它只會打包該專案自身的程式碼。所以打包速度會更快。

DLLPlugin 這個外掛是在一個額外獨立的webpack設定中建立一個只有dll的bundle,也就是說我們在專案根目錄下除了有webpack.config.js,還會新建一個webpack.dll.config.js檔案。webpack.dll.config.js作用是把所有的第三方庫依賴打包到一個bundle的dll檔案裡面,還會生成一個名為 manifest.json檔案。
該manifest.json的作用是用來讓 DllReferencePlugin 對映到相關的依賴上去的。

DllReferencePlugin 這個外掛是在webpack.config.js中使用的,該外掛的作用是把剛剛在webpack.dll.config.js中打包生成的dll檔案引用到需要的預編譯的依賴上來。什麼意思呢?就是說在webpack.dll.config.js中打包後比如會生成 vendor.dll.js檔案和vendor-manifest.json檔案,vendor.dll.js檔案包含所有的第三方庫檔案,vendor-manifest.json檔案會包含所有庫程式碼的一個索引,當在使用webpack.config.js檔案打包DllReferencePlugin外掛的時候,會使用該DllReferencePlugin外掛讀取vendor-manifest.json檔案,看看是否有該第三方庫。vendor-manifest.json檔案就是有一個第三方庫的一個對映而已。

所以說 第一次使用 webpack.dll.config.js 檔案會對第三方庫打包,打包完成後就不會再打包它了,然後每次執行 webpack.config.js檔案的時候,都會打包專案中本身的檔案程式碼,當需要使用第三方依賴的時候,會使用 DllReferencePlugin外掛去讀取第三方依賴庫。所以說它的打包速度會得到一個很大的提升。