你真的須要 Webpack DllPlugin 嗎?

若是你們看過一些 webpack4 優化的文章,必定會出現 dll 動態連接庫。它以配置之複雜讓衆多初學者記憶猶新。今天我會以一個學習者的角度去一步一步探討 webpack dll 的配置,最後得出一個完美的解決方案。html

前段時間寫了一篇詳解 webpack4 中易混淆知識點的文章,沒想到收穫了近 600 個贊,在這裏對各位老鐵抱拳感謝。上篇文章我費了不少時間去構思 demo 和原創做圖,就是想把一些概念完全講清楚,看評論區的反響我感受仍是作到了本身設定的目標。前端

若是你們看過一些 webpack4 優化的文章,必定會出現 dll 動態連接庫。它以配置之複雜讓衆多初學者記憶猶新。今天我會以一個學習者的角度去一步一步探討 webpack dll 的配置,最後得出一個完美的解決方案。vue

本文的內容和大部分講解 webpack4 優化文章的觀點不同,若是有不一樣的看法,歡迎在評論區和我討論。react

友情提示:本文章不是入門教程,不會費大量筆墨去描寫 webpack 的基礎配置,請讀者配合教程[源代碼](https://github.com/skychx/webpack_learn/tree/master/optimization)食用。

1. 基礎概念:dll 其實就是緩存

說實話我剛看見這個 dll 動態連接庫的時候,我真被鎮住了:這是什麼玩意?怎麼根本沒據說過?webpack

好學的我趕忙 Google 一下,在維基百科裏找到了標準定義:git

所謂動態連接,就是把一些常常會共享的代碼製做成 DLL 檔,當可執行文件調用到 DLL 檔內的函數時,Windows 操做系統纔會把 DLL 檔加載存儲器內,DLL 檔自己的結構就是可執行檔,當程序有需求時函數才進行連接。透過動態連接方式,存儲器浪費的情形將可大幅下降。github

唉,大家官方就是不說人話。web

我結合 webpack,從前端的角度翻譯一下:面試

具體到 webpack 這塊兒,就是事先把經常使用但又構建時間長的代碼提早打包好(例如 react、react-dom),取個名字叫 dll。後面再打包的時候就跳過原來的未打包代碼,直接用 dll。這樣一來,構建時間就會縮短,提升 webpack 打包速度。vue-cli

我盯着上面那句話看了三分鐘,什麼 DLL,什麼動態連接庫,在前端世界裏,不就是個緩存嗎!都是拿空間換時間。

注:在這裏狹義上能夠理解爲拿空間換時間,若是真的要探討 dll 背後的知識:動態連接庫和靜態連接庫,就又涉及到編譯器的知識了,具體講下去又是一篇新的文章了,因此暫時按下不表。

咱們對比一下 DLL 和前端常接觸的網絡緩存,一張表就看明白了:

DLL 緩存
1.把公共代碼打包爲 DLL 文件存到硬盤裏 1.把經常使用文件存到硬盤/內存裏
2.第二次打包時動態連接 DLL 文件,不從新打包 2.第二次加載時直接讀取緩存,不從新請求
3.打包時間縮短 3.加載時間縮短


因此在前端世界裏, DLL 就是個另類緩存。

2. DLL 手動配置:這麼多步根本記不住

剛開始咱們先不搞配置,咱們設想一下,若是讓你手動建立並管理緩存,你會怎麼作?

我想,你們的思路通常都是這樣的:

  1. 第一次請求的時候,把請求後的內容存儲起來
  2. 創建一個映射表,當後續有請求時,先根據這個映射表到看看要請求的內容有沒有被緩存,有的話就加載緩存,沒有就走正常請求流程(也就是所謂的緩存命中問題)
  3. 命中緩存後,直接從緩存中拿取內容,交給程序處理

主要流程無非這 3 步,想把事情搞大,能夠再加些權重啊,過時時間啊,多級緩存什麼的,但主要流程就是上面的 3 步。

通常咱們在開發的時候,瀏覽器,http 協議都幫咱們把這些操做封裝好了,咱們就記幾個參數調參就好了;可是 webpack dll 不同,它須要咱們手動實現上面 3 個步驟,因此就很是的無聊 + 繁瑣。

下面的代碼比較亂,由於我也沒打算好好講這些繞來繞去的配置,具體結構最好看我 github 上放出的示例源代碼,看不懂也沒事,後面有更好的解決方案。

看得煩就直接跳過下面的內容

第 1 步,咱們先要建立 dll 文件,這個至關於咱們對第一次的請求內容進行存儲,而後咱們還要建立一個映射表,告訴程序咱們把啥文件作成 dll 了(這個至關於第 2 步):

首先咱們寫一個建立 dll 文件的打包腳本,目的是把 react,react-dom打包成 dll 文件:

// 文件目錄:configs/webpack.dll.js
// 代碼太長能夠不看

'use strict';

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

module.exports = {
    mode: 'production',
    entry: {
        react: ['react', 'react-dom'],
    },
    // 這個是輸出 dll 文件
    output: {
        path: path.resolve(__dirname, '../dll'),
        filename: '_dll_[name].js',
        library: '_dll_[name]',
    },
    // 這個是輸出映射表
    plugins: [
        new webpack.DllPlugin({ 
            name: '_dll_[name]', // name === output.library
            path: path.resolve(__dirname, '../dll/[name].manifest.json'),
        })
    ]
};

打包腳本寫好了,咱們總得運行吧?因此咱們寫個運行腳本放在 package.json 的 scripts 標籤裏,這樣咱們運行 npm run build:dll 就能夠打包 dll 文件了:

// package.json

{
  "scripts": {
    "build:dll": "webpack --config configs/webpack.dll.js",
  },
}

第 3 步,連接 dll 文件,也就是告訴 webpack 能夠命中的 dll 文件,配置也是一大坨:

// 文件目錄:configs/webpack.common.js
// 代碼太長能夠不看

const path = require('path');
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin'); // 顧名思義,把資源加到 html 裏,那這個插件把 dll 加入到 index.html 裏
const webpack = require('webpack');
module.exports = {
  // ......
  plugins: [
    new webpack.DllReferencePlugin({
      // 注意: DllReferencePlugin 的 context 必須和 package.json 的同級目錄,要否則會連接失敗
      context: path.resolve(__dirname, '../'),
      manifest: path.resolve(__dirname, '../dll/react.manifest.json'),
    }),
    new AddAssetHtmlPlugin({
      filepath: path.resolve(__dirname, '../dll/_dll_react.js'),
    }),
  ]
}

爲了減小一些大型庫的二次打包時間,咱們在 3 個文件裏寫了一堆配置代碼,當心翼翼,如履薄冰,中間說不定還會由於做用域的問題連接失敗(對,說的就是我)。配置 dll 會給人帶來巨大的心理陰影,有沒有其餘方法下降咱們的心智負擔呢?

3. AutoDllPlugin:解放你的配置負擔

在第 2 小節裏我瘋狂勸退,就是想介紹這個插件:autodll-webpack-plugin,這個插件把上面那 3 坨代碼整合到一起,讓咱們擺脫繁瑣的配置,讓咱們看看這麼用吧:

// 文件目錄:configs/webpack.common.js

const path = require('path');
const AutoDllPlugin = require('autodll-webpack-plugin'); // 第 1 步:引入 DLL 自動連接庫插件

module.exports = {
  // ......
  plugins: [
        // 第 2 步:配置要打包爲 dll 的文件
        new AutoDllPlugin({
            inject: true, // 設爲 true 就把 DLL bundles 插到 index.html 裏
            filename: '[name].dll.js',
            context: path.resolve(__dirname, '../'), // AutoDllPlugin 的 context 必須和 package.json 的同級目錄,要否則會連接失敗
            entry: {
                react: [
                    'react',
                    'react-dom'
                ]
            }
        })
  ]
}

autodll-webpack-plugin  的使用方法和 webpack 的其餘 plugin 使用方式很是類似,和手動引入 dll 的方法比起來,簡單許多,並且這個插件以前是被 vue-cli 使用的,質量也是比較穩定的,你們能夠放心使用。

4. 拋棄 DLL:Vue & React 官方的共同選擇

第 3 節我說 autodll-webpack-plugin 以前被 vue-cli 使用,那意思是如今不用了?是否是有 bug 啊?這個還真不是。

學習 webpack 的時候,爲了借鑑一下業內優秀的框架的 webpack 配置,我專門看了 vue-cli 和 create-react-app 的源碼,可是卻沒有找到任何 dll 的配置痕跡。

這就很奇怪了,我以前翻過一些 nuxt.js 1.0 的源碼,裏面是有 dll 的配置代碼的,按道理來講 vue-cli 也應該有的,我就猜想是在某次升級中,把 dll 去掉了。因此我開始查找 commit 記錄,果真被我找到了:

白紙黑字,remove DLL option 3 個大字寫的清清楚楚

緣由是什麼呢?在這個 issue 裏尤雨溪解釋了去除的緣由:

dll option will be removed. Webpack 4 should provide good enough perf and the cost of maintaining DLL mode inside Vue CLI is no longer justified.

dll 配置將會被移除,由於 Webpack 4 的打包性能足夠好的,dll 沒有在 Vue ClI 裏繼續維護的必要了。

一樣的,在這個 PR 裏 create-react-app 裏也給出了相似的解釋:webpack 4 有着比 dll 更好的打包性能。

因此說,若是項目上了 webpack 4,再使用 dll 收益並不大。我拿實際項目的代碼試了一下,加入 dll 可能會有 1-2 s 的速度提高,對於總體打包時間能夠說能夠忽略不計。

Vue 和 React 官方 2018 都再也不使用 dll 了,如今 2019 年都快過去了,因此說我上面說的都沒用了,都不用學了,是否是感受鬆了一口氣(瘋狂暗示點贊)?

5. 比 DLL 更優秀的插件

dll 加速不明顯了,有沒有更好的替代品?在 AutoDllPlugin 的 README.md 裏,給咱們推薦了 HardSourceWebpackPlugin,初始配置更簡單,只須要一行代碼:

const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');

module.exports = {
  // ......
  plugins: [
    new HardSourceWebpackPlugin() // <- 直接加入這行代碼就行
  ]
}

這個插件加速有多明顯呢?我拿本文的試例代碼測試了一下,下圖是常規的打包時間,大概 900 ms:

加入 dll 優化後,打包時間爲 507 ms,縮短了 400 ms 左右:

只使用 HardSourceWebpackPlugin,再次打包時間縮短到 253 ms:

看相關的文檔,貌似這個技術直接放到了 webpack 5 裏,開箱即用。因此,雖然 dll 的配置你不用學了,可是 webpack 5 is coming......

6. 寫在最後

這篇文章很難說它是一篇教程,更多的是記錄了我學習 webpack 中的一個探索過程。說實話我把 dll 手動配完以爲我挺 nb 的,這麼複雜的配置我都能配好。

當我後續找到 autodll-webpack-plugin,並發現 dll 已經被拋棄時,其實仍是有些失望,以爲本身的以前的努力都白費了,不禁自主產生 學不動 的想法。可是當我仔細想了一下 dll 的原理,發現也就是那麼一會事兒,拿空間換時間,只不過配置複雜了一些。

因此這也提醒咱們,學習新知識的時候,不要專一於流程的配置和調參。由於流程終會簡化,參數(API)終會升級。要抓大放小,把精力放在最核心的內容上,由於核心的思想是最不容易過期的。

7.參考閱讀

面試必備!webpack 中那些最易混淆的 5 個知識點

webpack 官方文檔

autodll-webpack-plugin

HardSourceWebpackPlugin

相關文章
相關標籤/搜索