你真的需要 Webpack DllPlugin 嗎?

来源:https://www.cnblogs.com/skychx/archive/2020/05/18/webpack-dllplugin.html
-Advertisement-
Play Games

如果大家看過一些 webpack4 優化的文章,一定會出現 dll 動態鏈接庫。它以配置之複雜讓眾多初學者記憶猶新。今天我會以一個學習者的角度去一步一步探討 webpack dll 的配置,最後得出一個完美的解決方案。 ...


前段時間寫了一篇詳解 webpack4 中易混淆知識點的文章,沒想到收穫了近 600 個贊,在這裡對各位老鐵抱拳感謝。上篇文章我費了很多時間去構思 demo 和原創作圖,就是想把一些概念徹底講清楚,看評論區的反響我感覺還是做到了自己設定的目標。

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

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

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

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

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

好學的我趕緊 Google 一下,在維基百科里找到了標准定義:

所謂動態鏈接,就是把一些經常會共用的代碼製作成 DLL 檔,當可執行文件調用到 DLL 檔內的函數時,Windows 操作系統才會把 DLL 檔載入存儲器內,DLL 檔本身的結構就是可執行檔,當程式有需求時函數才進行鏈接。透過動態鏈接方式,存儲器浪費的情形將可大幅降低。

唉,你們官方就是不說人話。

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

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

我盯著上面那句話看了三分鐘,什麼 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 文件的打包腳本,目的是把 reactreact-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.jsonscripts 標簽里,這樣我們運行 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




最後推薦一下我的個人公眾號:「滷蛋實驗室」,平時會分享一些前端技術和數據分析的內容,大家感興趣的話可以關註一波:



您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...