dllplugin可能你不知道的坑

1,340 阅读6分钟

前言:初步认识dll之前,是由于我们的前端项目整个发布时间在5-8分钟左右,在安装依赖,构建项目,系统发布,整个过程中,构建项目的时间是占最多的,占据大概60%-80%的时间,于是我就想,我们的项目构建速度是否还有提升的空间,于是我就对我们现项目的整个webpack构建流程,一步步的过了一边,查阅了相关webpack的文档,总结了下,webpack的版本已经是4点几的版本,虽然5.0的时代即将到来,但4.0的版本目前应该还是主流的webpack版本,项目中:代码分块,懒加载,css分离,代码压缩,hash缓存等等常规优化已经存在项目的配置中,在查阅文档的过程中,我看到了一个叫dllplugin的插件,顿时感觉自己发现了新大陆(ps:可能很多同学之前已经听说这个插件,怪我比较out)。后面给大家介绍下,我在配置dllplugin过程中所遇到的坑。


第一步:简单介绍下dll的概念:

百度给出的解释是:动态链接库英文为

DLL
,是Dynamic Link Library的缩写。DLL是一个包含可由多个程序,同时使用的代码和数据的库。例如,在 Windows 操作系统中,Comdlg32.dll 执行与对话框有关的常见函数。因此,每个程序都可以使用该 DLL 中包含的功能来实现“打开”对话框。这有助于避免代码重用和促进内存的有效使用。


好吧,官方就是不喜欢说人话,从webpack前端的角度,我的理解是:我们可以将一些第三方的库(react、react-dom)等等库先打包出来,可以命名为react.dll.js的包,当实际构建项目的时候,我们可以观察到这些已经打包好的第三方库,避免重新构建第三方库,重而提升项目的构建速度。这么讲是不是好理解很多。


第二步:dllPlugin到底是怎么配置的(ps:现在不是很推荐大家按这种方式去配置dll,极其复杂)

首先:我们需要将一些第三库打包出来,以及后缀命名为manifest.json的文件这个文件有什么用,后续会有相关介绍。


const path = require('path');
const webpack = require('webpack');

module.exports = {
  mode: 'production',
  // 需要打包的第三方库
  entry: {
    vendors: ['html2canvas'],
    react: ['react', 'react-dom', 'react-router', 'react-sortablejs'],
    moment: ['moment'],
    echarts: ['echarts'],
    antd: ['antd'],
    pubsubJs: ['pubsub-js']
  },
  // 将打包好的文件输出到相关的目录下
  output: {
    path: path.resolve(__dirname, '../dll'),
    filename: '[name].[hash].dll.js',
    library: '[name]'
  },
  plugins: [
    // 在给定的 path 路径下创建一个名为 manifest.json 的文件。 这个文件包含了从 require 和 import 的request到模块 id 的映射
    new webpack.DllPlugin({
      context: path.join(__dirname, '../dll'),
      name: '[name]',
      path: path.resolve(__dirname, '../dll/[name].manifest.json')
    })
  ]
};

这个manifest.json文件其实就是根绝打包好后的第三库的context,生成的一个映射文件。


其次:我们需要将打包好的后的第三方库塞入那个由模版生成的index.html中,因为后续不会再去构建第三方库,所以得提前塞入,供其所有。


const AddAssetHtmlWebpackPlugin = require('add-asset-html-webpack-plugin');
const files = fs.readdirSync(path.resolve(__dirname, '../dll'));
files && files.forEach((file) => {
  if (/.*\.dll.js/.test(file)) {
    webpackProdConfig.plugins.push(new AddAssetHtmlWebpackPlugin({
      filepath: require.resolve(path.resolve(__dirname, '../dll', file))
    }));
  }})

怎么塞入用到了add-asset-html-webpack-plugin这个插件。

最后:构建的项目的时候webpack怎么知道哪些第三方库我不需要再次打包了呢?这就用到了manifest.json这个映射文件。


const files = fs.readdirSync(path.resolve(__dirname, '../dll'));
files && files.forEach((file) => {
  if (/.*\.manifest.json/.test(file)) {
    webpackProdConfig.plugins.push(new Webpack.DllReferencePlugin({
      context: path.join(__dirname, '../dll'),
      manifest: require(path.resolve(__dirname, '../dll', file))
    }));
  }
});


这里的配置还用到了DllReferencePlugin这个webpack内置的插件,这个插件是在 webpack 主配置文件中设置的, 这个插件把只有 dll 的 bundle(们)(dll-only-bundle(s)) 引用到需要的预编译的依赖。


终于配置完成,这样的配置简直就是反人类,webpack不是一直在优化插件的配置吗?怎么还会有这么复杂的插件配置。后面我又查看了一下文档,又发现一个autodll-webpack-plugin的插件,这个插件是对我第一次配置dll那种方式的简化,虽然这个插件不是webpack官方推荐的,但是也是可以用的,vue-cli 之前就使用过这个插件。接下介绍下autodll-webpack-plugin是如何配置dll的。

第一步:就简单的一步完成。

const AutoDllPlugin = require('autodll-webpack-plugin');
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',
          'react-router'
        ],
        mobx: [
          'mobx',
          'mobx-react'
        ],
        lodash: [
          'lodash'
        ]
      }
    })


这样的配置简直舒爽,一步到位,这才是我想使用的webapck插件。介绍了两种dll的配置方式,相信大家都会问,你咋还不说这dll到底可以提升多少项目的构建速度?

这里我就给大家一个结果吧:在用dll之后和之前,我构建了无数次的项目,两者的结果差距并不是很明显,提升的时间在5秒左右吧,针对整个项目构建时间,提升大概在1%左右,这样的提升对于整个项目的构建并没有实质性的改善,这样的结果,也让我非常的失望。这就是我为什么说vue-cli之前是有配置dll的,那意思现在就不用了吗?

是的,vue-cli早在2018-5-8就已经删除了dll相关的配置。

image.png

那为什么不用了?刘大大也给出了相关的解释:

image.png

大致的意思是:webpack4的性能已经足够好了,vue-cli将移除dll相关的配置,不在维护。

看到这,泪流满面,原来之前花了这么多时间配置,现在都已经没什么用了,看来平时对于知识的更新要加强警惕性了。

写到这:我发现我又回到了原点,目前我们项目的webpack版本已经到4了,是不是意味着性能足够好了,不用再配置dll了。还是有点不甘心,又去查了一些文档,终于又让我看到了一丝的曙光,接下来介绍下hard-source-webpack-plugin 插件。

hard-source-webpack-plugin这个插件也不是官方维护的插件,但是可靠性还是很高的。因为在webpack5.0,这个插件即将被维护。

这个插件的配置也很简单:

const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
plugins: [
    new HardSourceWebpackPlugin()
  ]


开发环境:使用hard-source-webpack-plugin之前的构建速度

image.png


开发环境:使用hard-source-webpack-plugin之后的第一次构建速度

image.png


开发环境:使用hard-source-webpack-plugin之后的第二次构建速度

image.png


从这三次构建速度来看,第三次的构建速度明显比第一次要快很多,为什么我要放三次构建的图呢,其实hard-source-webpack-plugin的原理还是缓存,第一次使用hard-source-webpack-plugin构建的时候,hard-source-webpack-plugin会将一些包写入缓存中,后续打包直接读取缓存,这样打包速度一下子快了很多。这就是拿空间换时间。比较失望的是,我在生产环境使用hard-source-webpack-plugin,还是没有明显提升打包速度。目前这个插件比较多的使用在开发环境。


写到这做个总结吧:前端技术的更新每天都是日新月异,我们没必要纠结一些工具的配置,要多去深入理解这个工具背后的理念,因为配置终究会被简化,理念相对没那么快被替代。