利用DllPlugin分割你的第三方庫

zhCN_超發表於2019-03-04

前言

小站利用React全家桶開發,在上線之後,發現第三方bundle(2Mb左右)下載用了近20秒。
最初發現的問題是nginx壓縮配置gzip沒有新增application/javascript這個MIME型別,但是仍然花了近5秒。

解決過程

初始

起初,我的配置檔案(webpack.dll.js)內容如下:

這個配置檔案獨立於你應用的webpack.dev.jswebpack.prod.js等配置檔案

const path = require(`path`);
const webpack = require(`webpack`);

const vendors = [
  `react`,
  `antd`,
  `lodash`
  ... // 其他第三方庫
]

const config = {
  entry: { vendors },
  output: {
    filename: `[name].js`,
    library: `[name]_library`, // 與 DllPlugin 的 name 保持一致
  },
  plugins: [
    new webpack.DllPlugin({
      path: path.resolve(`dll`, `[name].manifest.json`),
      name: `[name]_library`, // 這裡的命名要遵循變數命名規範,它是最終的包變數名
    })
  ]
}
複製程式碼

:只附關鍵程式碼,專案根目錄為project,更多配置引數請參見官網

執行這個配置檔案之後,這樣會在/project/dll目錄中生成兩個檔案:

  • vendors.js
  • vendors.manifest.json

我們需要在inedx.html頁面(也在專案根目錄下)中將vendors.js引入

<script src="/dll/vendors.js"></script>
複製程式碼

在你的webpack.dev.jswebpack.prod.js配置的plugins屬性中新增對這些包的引用:

const manifestFile = path.relove(`dll, `vendors.manifest.json`); // manifest.json 檔案地址
const config = {
  plugins: [
    new webpack.DllReferencePlugin({
      manifest: manifestFile
    })
  ]
}
複製程式碼

改進1

考慮到瀏覽器併發請求數預設為6個,我開始拆分第三方包(webpack.dll.js):

const Libs = {
  ui: [
    `antd`
  ],
  base: [
    `lodash`
  ],
  frame: [
    `react`
  ]
}
const config = {
  entry: { ...Libs },
  ... // 其他配置,同上
}
複製程式碼

執行之後,會生成三個.js.manifest.json檔案,同樣你需要將.js引用加入到你的index.js頁面:

<script src="/dll/ui.js"></script>
<script src="/dll/base.js"></script>
<script src="/dll/frame.js"></script>
複製程式碼

而在你的webpack.dev.jswebpack.prod.js中,你需要多次呼叫DllReferencePlugin外掛:

const manifestFileUi = path.relove(`dll`, `ui.manifest.json`);
const manifestFileBase = path.relove(`dll`, `base.manifest.json`);
const manifestFileFrame = path.relove(`dll`, `frame.manifest.json`);

const config = {
  ... // 其他配置
  plugins: [
    new webpack.DllReferencePlugin({
      manifest: manifestFileUi
    }),
    new webpack.DllReferencePlugin({
      manifest: manifestFileBase
    }),
    new webpack.DllReferencePlugin({
      manifest: manifestFileFrame
    })
  ... // 其它 plugin
  ]
}
複製程式碼

建議根據 Libs 變數寫個函式迴圈生成

改進2

我發現antdlodash打包出來仍然很大,那麼就按需載入(webpack.dll.js):

const Libs = {
  ui: [
    `antd/es/button`,
    `antd/es/input`,
    ... // 其他你專案中用到的 UI 元件
  ],
  base: [
    `lodash/fp/get`,
    `lodash/fp/set`,
    ... // 其他你專案中用到的函式
  ]
  ... // frame
}
複製程式碼

最終的載入時間為1秒左右。

問題

lodash按需載入的話,它使用方法改變如下:

// 之前
import _ from `lodash`;
_.get(...);

// 之後
import get from `lodash/fp/get;
get(...)
複製程式碼

但是第二種方法總是返回undefined,所以我改回了第一種使用方法,但是仍然是按需打包,問題有待查詢。

結束

謝謝閱讀,如有謬誤,懇請斧正。

相關文章