效果展示
打包時間:縮短了 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'