辛辛苦苦学会的 webpack dll 配置,可能已通过时了

前段时间写了一篇详解 webpack4 中易混淆知识点的文章,没想到收获了近 600 个赞,在这里对各位老铁抱拳感谢。上篇文章我费了不少时间去构思 demo 和原创做图,就是想把一些概念完全讲清楚,看评论区的反响我感受仍是作到了本身设定的目标。javascript

若是你们看过一些 webpack4 优化的文章,必定会出现 dll 动态连接库。它以配置之复杂让众多初学者记忆犹新。今天我会以一个学习者的角度去一步一步探讨 webpack dll 的配置,最后得出一个完美的解决方案。html

本文的内容和大部分讲解 webpack4 优化文章的观点不同,若是有不一样的看法,欢迎在评论区和我讨论。前端


友情提示:本文章不是入门教程,不会费大量笔墨去描写 webpack 的基础配置,请读者配合教程[源代码](https://github.com/skychx/webpack_learn/tree/master/optimization)食用。 vue


1. 基础概念:dll 其实就是缓存

说实话我刚看见这个 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 就是个另类缓存。

2. DLL 手动配置:这么多步根本记不住

刚开始咱们先不搞配置,咱们设想一下,若是让你手动建立并管理缓存,你会怎么作?

我想,你们的思路通常都是这样的:

  1. 第一次请求的时候,把请求后的内容存储起来
  2. 创建一个映射表,当后续有请求时,先根据这个映射表到看看要请求的内容有没有被缓存,有的话就加载缓存,没有就走正常请求流程(也就是所谓的缓存命中问题)
  3. 命中缓存后,直接从缓存中拿取内容,交给程序处理

主要流程无非这 3 步,想把事情搞大,能够再加些权重啊,过时时间啊,多级缓存什么的,但主要流程就是上面的 3 步。

通常咱们在开发的时候,浏览器,http 协议都帮咱们把这些操做封装好了,咱们就记几个参数调参就好了;可是 webpack dll 不同,它须要咱们手动实现上面 3 个步骤,因此就很是的无聊 + 繁琐。

下面的代码比较乱,由于我也没打算好好讲这些绕来绕去的配置,具体结构最好看我 github 上放出的示例源代码看不懂也没事,后面有更好的解决方案

看得烦就直接跳过下面的内容

第 1 步,咱们先要建立 dll 文件,这个至关于咱们对第一次的请求内容进行存储,而后咱们还要建立一个映射表,告诉程序咱们把啥文件作成 dll 了(这个至关于第 2 步):

首先咱们写一个建立 dll 文件的打包脚本,目的是把 reactreact-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.jsonscripts 标签里,这样咱们运行 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 会给人带来巨大的心理阴影,有没有其余方法下降咱们的心智负担呢?

3. AutoDllPlugin:解放你的配置负担

在第 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 使用的,质量也是比较稳定的,你们能够放心使用。

4. 抛弃 DLL:Vue & React 官方的共同选择

第 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 年都快过去了,因此说我上面说的都没用了,都不用学了,是否是感受松了一口气(疯狂暗示点赞)?

5. 比 DLL 更优秀的插件

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......

6. 写在最后

这篇文章很难说它是一篇教程,更多的是记录了我学习 webpack 中的一个探索过程。说实话我把 dll 手动配完以为我挺 nb 的,这么复杂的配置我都能配好。

当我后续找到 autodll-webpack-plugin,并发现 dll 已经被抛弃时,其实仍是有些失望,以为本身的以前的努力都白费了,不禁自主产生 学不动 的想法。可是当我仔细想了一下 dll 的原理,发现也就是那么一会事儿,拿空间换时间,只不过配置复杂了一些。

因此这也提醒咱们,学习新知识的时候,不要专一于流程的配置和调参。由于流程终会简化,参数(API)终会升级。要抓大放小,把精力放在最核心的内容上,由于核心的思想是最不容易过期的。

7.参考阅读

面试必备!webpack 中那些最易混淆的 5 个知识点

webpack 官方文档

autodll-webpack-plugin

HardSourceWebpackPlugin


最后打个广告,业余写一些数据可视化科普向的文章,目前一周一篇,内容非技术向。目前在写不须要写代码的爬虫教程,以为不错的话能够推荐给非技术同事。公众号 ID 是 sky-chx,你们感兴趣的能够关注一波。