效果展示
打包时间:缩短了 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'