前段時間寫了一篇詳解 webpack4 中易混淆知識點的文章,沒想到收穫了近 600 個贊,在這裏對各位老鐵抱拳感謝。上篇文章我費了不少時間去構思 demo 和原創做圖,就是想把一些概念完全講清楚,看評論區的反響我感受仍是作到了本身設定的目標。javascript
若是你們看過一些 webpack4 優化的文章,必定會出現 dll 動態連接庫。它以配置之複雜讓衆多初學者記憶猶新。今天我會以一個學習者的角度去一步一步探討 webpack dll 的配置,最後得出一個完美的解決方案。html
本文的內容和大部分講解 webpack4 優化文章的觀點不同,若是有不一樣的看法,歡迎在評論區和我討論。前端
友情提示:本文章不是入門教程,不會費大量筆墨去描寫 webpack 的基礎配置,請讀者配合教程[源代碼](https://github.com/skychx/webpack_learn/tree/master/optimization)食用。 vue
說實話我剛看見這個 dll 動態連接庫的時候,我真被鎮住了:這是什麼玩意?怎麼根本沒據說過?java
好學的我趕忙 Google 一下,在維基百科裏找到了標準定義:react
所謂動態連接,就是把一些常常會共享的代碼製做成 DLL 檔,當可執行文件調用到 DLL 檔內的函數時,Windows 操做系統纔會把 DLL 檔加載存儲器內,DLL 檔自己的結構就是可執行檔,當程序有需求時函數才進行連接。透過動態連接方式,存儲器浪費的情形將可大幅下降。webpack
唉,大家官方就是不說人話。git
我結合 webpack,從前端的角度翻譯一下:github
具體到 webpack 這塊兒,就是事先把經常使用但又構建時間長的代碼提早打包好(例如 react、react-dom),取個名字叫 dll。後面再打包的時候就跳過原來的未打包代碼,直接用 dll。這樣一來,構建時間就會縮短,提升 webpack 打包速度。web
我盯着上面那句話看了三分鐘,什麼 DLL,什麼動態連接庫,在前端世界裏,不就是個緩存嗎!都是拿空間換時間。
注:在這裏狹義上能夠理解爲拿空間換時間,若是真的要探討
dll
背後的知識:動態連接庫和靜態連接庫,就又涉及到編譯器的知識了,具體講下去又是一篇新的文章了,因此暫時按下不表。
咱們對比一下 DLL 和前端常接觸的網絡緩存,一張表就看明白了:
DLL | 緩存 |
---|---|
1.把公共代碼打包爲 DLL 文件存到硬盤裏 | 1.把經常使用文件存到硬盤/內存裏 |
2.第二次打包時動態連接 DLL 文件,不從新打包 | 2.第二次加載時直接讀取緩存,不從新請求 |
3.打包時間縮短 | 3.加載時間縮短 |
因此在前端世界裏, DLL 就是個另類緩存。
剛開始咱們先不搞配置,咱們設想一下,若是讓你手動建立並管理緩存,你會怎麼作?
我想,你們的思路通常都是這樣的:
主要流程無非這 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 會給人帶來巨大的心理陰影,有沒有其餘方法下降咱們的心智負擔呢?
在第 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 使用的,質量也是比較穩定的,你們能夠放心使用。
第 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 年都快過去了,因此說我上面說的都沒用了,都不用學了,是否是感受鬆了一口氣(瘋狂暗示點贊)?
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......
這篇文章很難說它是一篇教程,更多的是記錄了我學習 webpack 中的一個探索過程。說實話我把 dll 手動配完以爲我挺 nb 的,這麼複雜的配置我都能配好。
當我後續找到 autodll-webpack-plugin
,並發現 dll 已經被拋棄時,其實仍是有些失望,以爲本身的以前的努力都白費了,不禁自主產生 學不動
的想法。可是當我仔細想了一下 dll 的原理,發現也就是那麼一會事兒,拿空間換時間,只不過配置複雜了一些。
因此這也提醒咱們,學習新知識的時候,不要專一於流程的配置和調參。由於流程終會簡化,參數(API)終會升級。要抓大放小,把精力放在最核心的內容上,由於核心的思想是最不容易過期的。
最後打個廣告,業餘寫一些數據可視化科普向的文章,目前一週一篇,內容非技術向。目前在寫不須要寫代碼的爬蟲教程,以爲不錯的話能夠推薦給非技術同事。公衆號 ID 是 sky-chx,你們感興趣的能夠關注一波。