webpack使用-詳解DllPlugin

前言

(時光飛逝,轉眼又偷懶了一個多月)node

什麼是DLL

DLL(Dynamic Link Library)文件爲動態連接庫文件,在Windows中,許多應用程序並非一個完整的可執行文件,它們被分割成一些相對獨立的動態連接庫,即DLL文件,放置於系統中。當咱們執行某一個程序時,相應的DLL文件就會被調用。

舉個例子:不少產品都用到螺絲,可是工廠在生產不一樣產品時,不須要每次連帶着把螺絲也生產出來,由於螺絲能夠單獨生產,並給多種產品使用。在這裏螺絲的做用就能夠理解爲是dll。react

爲何要使用Dll

一般來講,咱們的代碼均可以致少簡單區分紅業務代碼第三方庫。若是不作處理,每次構建時都須要把全部的代碼從新構建一次,耗費大量的時間。而後大部分狀況下,不少第三方庫的代碼並不會發生變動(除非是版本升級),這時就能夠用到dll:把複用性較高的第三方模塊打包到動態連接庫中,在不升級這些庫的狀況下,動態庫不須要從新打包,每次構建只從新打包業務代碼webpack

仍是上面的例子:把每次構建,當作是生產產品的過程,咱們把生產螺絲的過程先提取出來,以後咱們無論調整產品的功能或者設計(對應於業務代碼變動),都沒必要重複生產螺絲(第三方模塊不須要重複打包);除非是產品要使用新型號的螺絲(第三方模塊須要升級),才須要去從新生產新的螺絲,而後接下來又能夠專一於調整產品自己。web

基本用法

使用dll時,能夠把構建過程分紅dll構建過程和主構建過程(實質也就是如此),因此須要兩個構建配置文件,例如叫作webpack.config.jswebpack.dll.config.jsjson

1. 使用DLLPlugin打包須要分離到動態庫的模塊

DllPluginwebpack內置的插件,不須要額外安裝,直接配置webpack.dll.config.js文件:redux

module.exports = {=
  entry: {
    // 第三方庫
    react: ['react', 'react-dom', 'react-redux']
  },
  output: {
    // 輸出的動態連接庫的文件名稱,[name] 表明當前動態連接庫的名稱,
    filename: '[name].dll.js',
    path: resolve('dist/dll'),
    // library必須和後面dllplugin中的name一致 後面會說明
    library: '[name]_dll_[hash]'
  },
  plugins: [
  // 接入 DllPlugin
    new webpack.DllPlugin({
      // 動態連接庫的全局變量名稱,須要和 output.library 中保持一致
      // 該字段的值也就是輸出的 manifest.json 文件 中 name 字段的值
      name: '[name]_dll_[hash]',
      // 描述動態連接庫的 manifest.json 文件輸出時的文件名稱
      path: path.join(__dirname, 'dist/dll', '[name].manifest.json')
    }),
  ]
}

咱們先來看看,這一步到底作了什麼。執行:webpack --config webpack.dll.config,而後到指定的輸出文件夾查看輸出:數組

  1. react.dll文件裏是使用數組保存的模塊,索引值就做爲id;
  2. react.manifest.json文件裏,是用來描述對應的dll文件裏保存的模塊

裏暴露出剛剛構建的全部模塊,以下:瀏覽器

{
  "name":"react_dll_553e24e2c44987d2578f",
  "content":{
    "./node_modules/webpack/node_modules/process/browser.js":{"id":0,"meta":{}},"./node_modules/react/node_modules/fbjs/lib/invariant.js":{"id":1,"meta":{}},"./node_modules/react/lib/Object.assign.js":{"id":2,"meta":{}},"./node_modules/react/node_modules/fbjs/lib/warning.js":{"id":3,"meta":{}}
    //省略類似代碼
  }
}

2. 在主構建配置文件使用動態庫文件

webpack.config中使用dll要用到DllReferencePlugin,這個插件經過引用 dll 的 manifest 文件來把依賴的名稱映射到模塊的 id 上,以後再在須要的時候經過內置的 webpack_require 函數來 require 他們.app

new webpack.DllReferencePlugin({
    context: __dirname,
    manifest: require('./dist/dll/react.manifest.json')
  }),

第一步產出的manifest文件就用在這裏,給主構建流程做爲查找dll的依據:DllReferencePlugin去 manifest.json 文件讀取 name 字段的值,把值的內容做爲在從全局變量中獲取動態連接庫中內容時的全局變量名,所以:在 webpack_dll.config.js 文件中,DllPlugin 中的 name 參數必須和 output.library 中保持一致。dom

3. 在入口文件引入dll文件。

生成的dll暴露出的是全局函數,所以還須要在入口文件裏面引入對應的dll文件。

<body>
  <div id="app"></div>
  <!--引用dll文件-->
  <script src="../../dist/dll/react.dll.js" ></script>
</body>

做用

首先從前面的介紹,至少能夠看出dll的兩個做用

  1. 分離代碼,業務代碼和第三方模塊能夠被打包到不一樣的文件裏,這個有幾個好處:

    • 避免打包出單個文件的大小太大,不利於調試
    • 將單個大文件拆成多個小文件以後,必定狀況下有利於加載(不超出瀏覽器一次性請求的文件數狀況下,並行下載確定比串行快)
  2. 提高構建速度。第三方庫沒有變動時,因爲咱們只構建業務相關代碼,相比所有從新構建天然要快的多。

注意事項

從前面能夠看到dll帶來的優勢,但並不意味着咱們就應該把除業務代碼外的全部代碼所有都丟到dll中,舉一個例子:
1.對於lodash這種第三方庫,正確的用法是隻去import所需的函數(用什麼引什麼),例如:

// 正確用法
import isPlainObject from 'lodash/isPlainObject'

//錯誤用法
import { isPlainObject } from 'lodash'

這兩種寫法的差異在於,打包時webpack會根據引用去打包依賴的內容,因此第一種寫法,webpack只會打包lodash的isPlainObject庫,第二種寫法卻會打包整個lodash。如今假設在項目中只是用到不一樣模塊對lodash裏的某幾個函數而且沒有對於某個函數重複使用很是屢次,那麼這時候把lodash添加到dll中,帶來的收益就並不明顯,反而致使2個問題:

  1. 因爲打包了整個lodash,而致使打包後的文件總大小(注意是總大小)比原先還要大
  2. 在dll打包太多內容也須要耗費時間,雖然咱們通常只在第三方模塊更新以後才進行從新預編譯(就是dll打包的過程),可是若是這個時間太長的話體驗也很差、

實踐與反思

放一張本身在一個比較大的項目中單純使用dll以後的收益,提取的內容是 react相關的第三方庫,和fish組件,構建時間從120s下降到80s左右(固然這個時間仍是有點恐怖),構建前appjs的大小是680kb,拆分業務代碼和第三方代碼分別是400kb和380kb(這就是拆分後大小大於拆分前大小的例子),從這一點來看,對於常見第三方庫是否要放進dll可能比較明確(好比react系列打包通常確定不虧),可是還有一些就要結合具體的項目內容來進行判斷和取捨。(強烈推薦使用webpack-bundle-analyzer插件進行性能分析)
圖片描述
圖片描述
圖片描述

總結

本文介紹了Dllplugin的思想,基本用法和應用場景(關於使用的部分更詳細的內容能夠看官方文檔),結合我的的一些實踐經驗,對於常見第三方庫是否要放進dll可能比較明確(好比react系列打包通常確定不虧),可是還有一些就要結合具體的項目內容來判斷,例如我上面的實踐的例子就說明目前的拆分還不夠好。這一塊也歡迎你們一塊兒探討。若是內容有錯誤的地方歡迎指出(以爲看着不理解不舒服想吐槽也徹底沒問題);若是對你有幫助,歡迎點贊和收藏,轉載請徵得贊成後著明出處,若是有問題也歡迎私信交流,主頁有郵箱地址

相關文章
相關標籤/搜索