项目优化之webpack.DllPlugin

452 阅读3分钟

DLL

动态链接库(Dynamic Link Library 或者 Dynamic-link Library),将可以共享,并且不经常改变的代码,抽取成一个共享的库,第二次打包时动态链接 DLL 文件,不重新打包,缩短打包时间。

使用场景

在使用 webpack 开发过程中,对于大量第三方包(如vue、vue-router、axios、elementUI等),并不是经常发生变化。每次编译时都重新构建这些资源,浪费了大量的时间。借助 DLL 思路,webpack 中引入了 DllPlugin 和 DllReferencePlugin ,允许拆分指定的第三方包、并创建单独的包,生成 manifest.json 二次构建跳过这部分编译,并教 Webpack 将它们引用到该包。以此提高整体构建速度。

第一步:指定需要拆分的包,形成 DLL 库
第二步:告知webpack,命中 DLL 库文件

配置

配置需要拆分的包dll.config.js(以开发环境development为例,production环境请自行修改对应配置):

'use strict';
const path = require('path');
const webpack = require('webpack');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = {
  mode: 'development',  // 环境配置,开发环境用development,打包时使用production
  entry: {  //配置拆分的包,可配置多入口
    vue: ['vue', 'axios'],
    antd: ['ant-design-vue', '@ant-design/icons-vue'],
  },
  output: {
    path: path.resolve(__dirname, 'dll'),
    filename: '[name].js',
    library: '[name]_[hash]'
  },
  plugins: [
    new CleanWebpackPlugin({ cleanAfterEveryBuildPatterns: ['dll'] }),  //清空之前的dll文件
    new webpack.DllPlugin({
      name: '[name]_[hash]',  // 需要与output.library保持一致
      path: path.resolve(__dirname, 'dll', '[name]-manifest.json'),
      format: true,   // 使生成的manifest.json文件格式化
      context: __dirname  //执行上下文
    })
  ]
};

在package.json加命令"dll": "webpack --config ./dll.config.js",执行npm run dll生成DLL文件

使用DllReferencePlugin链接DLL文件。在webpack.config.js文件中配置plugins

const path = require('path');
const fs = require('fs');
const webpack = require('webpack');
const HtmlWebpackTagsPlugin = require('html-webpack-tags-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');

const dllConfig = require('./dll.config.js');
module.exports = {
    ...
    plugins:[
        ...
    ].concat(useDll())
}

// 对DLL文件做链接处理
function useDll() {
  if (!devMode) {  // 做好环境区分
    return [];
  }
  const hasDll = fs.existsSync(path.resolve('./dll'));
  if (!hasDll) {
    console.log('如果构建缓慢请使用dll缓存,请使用npm run dll');
    return [];
  }
  const dllPlugins = [
    ...Object.keys(dllConfig.entry).map(name => {
      return new webpack.DllReferencePlugin({
        context: __dirname,
        manifest: path.resolve(__dirname, 'dll', name + '-manifest.json')  //与dll.config中webpack.DllPlugin  path对应
      });
    }),
    // 将构建好的dll文件copy到dist/dll中,有些框架项目会将pulick目录自动拷贝到dist下,而你又正好把dll文件放在pulick目录下,这种情况可忽略这一步
    new CopyWebpackPlugin({
      patterns: [{ from: 'dll', to: 'dll' }]
    }),
    // 生成文件链接自动插入到index.html中,不推荐手动添加,很容易出错
    new HtmlWebpackTagsPlugin({
      tags: Object.keys(dllConfig.entry).map(name => `dll/${name}.js`),
      append: false   //插入到<head></head>前面,防止加载顺序出错 
    })
  ];
  return dllPlugins;
}

注意点: 如果依赖更新,需要重新生成dll文件

通常情况下,copy-webpack-plugin、html-webpack-tags-plugin这两个插件可用add-asset-html-webpack-plugin代替,但是由于公司内网原因导致这个插件无法下载,所以找了这个平替方案

const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin')
...
new AddAssetHtmlPlugin({
   // dll文件位置
   filepath: path.resolve(__dirname, dll ,'./*.js')
})
...

其他更优方案

可以看到,这个手动配置DLL是十分繁琐复杂的,而且容易出错。那么重点来了,有没有轮子可以复用呢?当然有,以下介绍几种比较靠谱、好用的轮子。

1、如果你的项目是基于vueCli3搭建,可借助插件vue-cli-plugin-dll快速配置
2、如果你的项目或者框架是基于webpack<5,可借助插件autodll-webpack-plugin,当依赖更新时会自动生成新的dll文件
3、如果你的项目或者框架基于webpack5,那dll已经不是那么推荐了,反而hard-source-webpack-plugin更为适用,且效率更高。Webpack5 中已对该部分进行了官方实现cache,只需简单配置cache: { type: 'filesystem' },实现秒级启动(当然第一次运行时间还是一样的)