效果展示
打包時間:縮短了 26.296s-20.586s=5.71s
先看兩組測試數據,第一組是沒有使用DllPlugin的打包測試數據,測量三次取平均值是26.296s
(25.72+25.56+27.61)/3≈26.296s


第二組是使用了DllPlugin的打包測試數據,測量三次取平均值是20.586s
(20.62+21.31+19.83)/3≈20.586s 


打包體積:減少了 8.72M-4.8M=3.92M
沒用動態庫之前是8.72M
用了動態庫之后是1.8M+2958K≈4.8M

減少的原因是避免了在業務代碼中重復引入第三方工具包。
為什么會快?
我們的項目代碼,可以分為第三方工具包和業務代碼,第三方工具包一般比較成熟,用webpack打包編譯過,無需每次項目構建時都再次打包。可以把這部分代碼從剝離出去,通過外鏈script標簽引入,每次構建,只打包業務代碼。所以能縮短整體打包時間。
如何實現
要想實現這樣的效果,你需要在現有項目的基礎上,做如下配置:
第一步,安裝依賴
yarn add -D assets-webpack-plugin clean-webpack-plugin webpack-bundle-analyzer
第二步,編寫生成dll庫的webpack配置文件
const path = require("path");
const webpack = require("webpack");
const WebpackBar = require("webpackbar");
const AssetsPlugin = require("assets-webpack-plugin");
const { CleanWebpackPlugin } = require("clean-webpack-plugin"); // 打包前清空dll文件夾
// 讀取package.json里的運行依賴包
const pkg = require("../package.json");
let dependencies = Object.keys(pkg.dependencies) || [];
dependencies = dependencies.length > 0 ? dependencies : [];
console.log("dll", dependencies);
module.exports = {
entry: {
dll: dependencies,
},
mode: "production",
output: {
path: path.resolve(__dirname, "../dll"),
filename: "[name]_[hash:6].js",
library: "[name]_[hash:6]", // 暴露給外部使用
// libraryTarget 指定如何暴露內容,缺省時就是 var
},
plugins: [
new CleanWebpackPlugin({
cleanOnceBeforeBuildPatterns: [path.resolve(__dirname, "../dll/*.*")],
}),
new webpack.DllPlugin({
path: path.resolve(__dirname, "../dll", "[name]-manifest.json"),
name: "[name]_[hash:6]", // name和library一致
}),
// 把帶hash的dll.js插入到index.html中,和html-webpack-plugin插件配合使用,告訴html-webpack-plugin插入的dll.js文件名稱
new AssetsPlugin({
filename: "dll-config.json",
path: "./dll/",
}),
// webpackbar可以在打包時實時顯示打包進度
new WebpackBar(),
],
};
在package.json中,添加生成dll庫的指令:
"scripts": { "build:dll": "webpack --config webpack/dll.js", },
生成動態庫


第三步:在index.html靜態模板中,加載動態庫
<!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, viewport-fit=cover" /> <meta name="theme-color" content="#000000" /> <meta name="keywords" content="" /> <meta name="description" content="" /> <title></title> </head> <body> <noscript>You need to enable JavaScript to run this app.</noscript> <div id="root"></div> <script src="//at.alicdn.com/t/font_1343302_nuzqn1v7zae.js"></script> <!-- 插入動態庫 --> <% if (htmlWebpackPlugin.options.dllJsName) { %> <script src="<%= htmlWebpackPlugin.options.dllJsName %>"></script> <% } %> <!-- iconfont svg地址 --> </body> </html>
第四步:在webpack.base.js中,配置動態庫加載和庫映射文件路徑
// 是否為本地開發環境 const isDev = process.env.NODE_ENV === "development"; // 根目錄 const basename = process.env.BASE_NAME ? `${process.env.BASE_NAME}/` : "/"; const publicPath = isDev ? "/" : `/${basename}`; // 這里的路徑與webpack文件夾下的dll.js配置文件中的路徑保持一致 const dllConfig = require("../dll/dll-config.json"); const manifest = require("../dll/dll-manifest.json"); module.exports = { plugins: [ new HtmlPlugin({ template: path.resolve(rootPath, "./index.html"), favicon: path.resolve(rootPath, "./favicon.ico"), // index.html中加載dll的script標簽的src地址 dllJsName: isDev ? `${publicPath}dll/${dllConfig.dll.js}` : "", // html壓縮 minify: { collapseWhitespace: true, preserveLineBreaks: true, }, }), // 加載生成的dll庫 isDev ? new webpack.DllReferencePlugin({ manifest, }) : () => {}, ], };
打包構建時,查看打包內容和大小的配置
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPlugin; // 包分析工具
module.module.exports = () => {
return merge(webpackBaseConfig, {
plugins: [
process.argv.indexOf("--pa") !== -1
? new BundleAnalyzerPlugin()
: () => {},
],
});
};
沒使用動態庫之前入口文件大小是1.43M

使用了動態庫之后入口文件大小是648K

問題與解答
首頁加載速度對比:使用動態庫之后,首頁加載速度變慢了3.08 - 2.51 =0.57s
使用動態庫之前,首頁加載時間是2.51s

使用動態庫之后,首頁加載時間是3.08s

使用了動態庫之后,如何不拖慢首頁的加載速度?
首頁加載速度變慢了一些,是由於打包的第三方庫,不再是按需加載,而是在首頁一次性加載,要改善這種情況,有兩條思路:
1.縮小打包體積,只把每個頁面都會用到的三方工具打包進動態庫, 還有對打包之后的內容進行gzip壓縮。
2.只在開發環境使用動態庫功能。
Dll和External的區別
對於如下的引用, Dll直接將庫的應用指向xxx庫,不會再把xxx/lib/module打包,而External則認為 import Foo from 'xxx' 和 import AA from 'xxx/lib/module',是引用了兩個不同的庫,因此xxx在項目中已經存在的情況下, xxx/lib/module還會被打包進項目。用import Foo from 'xxx/lib/module'這樣的方式引用模塊,使用動態庫是比較吃虧的。
import Foo from 'xxx/lib/module'
