效果展示

打包时间:缩短了 26.296s-20.586s=5.71s

先看两组测试数据,第一组是没有使用DllPlugin的打包测试数据,测量三次取平均值是26.296s
(25.72+25.56+27.61)/3≈26.296s
WebPack系列--开启HappyPack之后,再将项目打包速度缩短5秒-LMLPHP

WebPack系列--开启HappyPack之后,再将项目打包速度缩短5秒-LMLPHP

 WebPack系列--开启HappyPack之后,再将项目打包速度缩短5秒-LMLPHP

第二组是使用了DllPlugin的打包测试数据,测量三次取平均值是20.586s
(20.62+21.31+19.83)/3≈20.586s 
WebPack系列--开启HappyPack之后,再将项目打包速度缩短5秒-LMLPHP

 WebPack系列--开启HappyPack之后,再将项目打包速度缩短5秒-LMLPHP

 WebPack系列--开启HappyPack之后,再将项目打包速度缩短5秒-LMLPHP

打包体积:减少了 8.72M-4.8M=3.92M

没用动态库之前是8.72M
WebPack系列--开启HappyPack之后,再将项目打包速度缩短5秒-LMLPHP

用了动态库之后是1.8M+2958K≈4.8M
WebPack系列--开启HappyPack之后,再将项目打包速度缩短5秒-LMLPHP WebPack系列--开启HappyPack之后,再将项目打包速度缩短5秒-LMLPHP

减少的原因是避免了在业务代码中重复引入第三方工具包。
WebPack系列--开启HappyPack之后,再将项目打包速度缩短5秒-LMLPHP

为什么会快?

我们的项目代码,可以分为第三方工具包和业务代码,第三方工具包一般比较成熟,用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",
},

生成动态库

WebPack系列--开启HappyPack之后,再将项目打包速度缩短5秒-LMLPHPWebPack系列--开启HappyPack之后,再将项目打包速度缩短5秒-LMLPHP

第三步:在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

WebPack系列--开启HappyPack之后,再将项目打包速度缩短5秒-LMLPHP

 使用了动态库之后入口文件大小是648K

WebPack系列--开启HappyPack之后,再将项目打包速度缩短5秒-LMLPHP

 问题与解答

首页加载速度对比:使用动态库之后,首页加载速度变慢了3.08 - 2.51 =0.57s

使用动态库之前,首页加载时间是2.51s

WebPack系列--开启HappyPack之后,再将项目打包速度缩短5秒-LMLPHP

使用动态库之后,首页加载时间是3.08s

WebPack系列--开启HappyPack之后,再将项目打包速度缩短5秒-LMLPHP

使用了动态库之后,如何不拖慢首页的加载速度?

首页加载速度变慢了一些,是由于打包的第三方库,不再是按需加载,而是在首页一次性加载,要改善这种情况,有两条思路:

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'
04-30 01:28