Webpack的dll功能

最近使用Webpack遇到了一个坑。html

咱们构建前端项目的时候,每每但愿第三方库(vendors)和本身写的代码能够分开打包,由于第三方库每每不须要常常打包更新。对此Webpack的文档建议用CommonsChunkPlugin来单独打包第三方库。前端

entry: {
  vendor: ["jquery", "other-lib"],
  app: "./entry"
}
new CommonsChunkPlugin({
  name: "vendor",

  // filename: "vendor.js"
  // (Give the chunk a different name)

  minChunks: Infinity,
  // (with more entries, this ensures that no other module
  //  goes into the vendor chunk)
})

一般为了对抗缓存,咱们会给售出文件的文件名中加入hash的后缀——可是——咱们编辑了app部分的代码后,从新打包,发现vendor的hash也变化了!node

两次打包,并无修改vendor部分的代码,然而hash变化了

这么一来,意味着每次发布版本的时候,vendor代码都要刷新,即便我并无修改其中的代码。这样并不符合咱们分开打包的初衷。react

带着问题我浏览了Github上的讨论,发现了一个神器:dll。jquery

Dll是Webpack最近新加的功能,我在网上并无找到什么中文的介绍,因此在这里我就简单介绍一下。webpack

Dll这个概念应该是借鉴了Windows系统的dll。一个dll包,就是一个纯纯的依赖库,它自己不能运行,是用来给你的app引用的。git

打包dll的时候,Webpack会将全部包含的库作一个索引,写在一个manifest文件中,而引用dll的代码(dll user)在打包的时候,只须要读取这个manifest文件,就能够了。github

这么一来有几个好处:web

  1. Dll打包之后是独立存在的,只要其包含的库没有增减、升级,hash也不会变化,所以线上的dll代码不须要随着版本发布频繁更新。json

  2. App部分代码修改后,只须要编译app部分的代码,dll部分,只要包含的库没有增减、升级,就不须要从新打包。这样也大大提升了每次编译的速度。

  3. 假设你有多个项目,使用了相同的一些依赖库,它们就能够共用一个dll。

如何使用呢?

首先要先创建一个dll的配置文件,entry只包含第三方库:

const webpack = require('webpack');

const vendors = [
  'antd',
  'isomorphic-fetch',
  'react',
  'react-dom',
  'react-redux',
  'react-router',
  'redux',
  'redux-promise-middleware',
  'redux-thunk',
  'superagent',
];

module.exports = {
  output: {
    path: 'build',
    filename: '[name].[chunkhash].js',
    library: '[name]_[chunkhash]',
  },
  entry: {
    vendor: vendors,
  },
  plugins: [
    new webpack.DllPlugin({
      path: 'manifest.json',
      name: '[name]_[chunkhash]',
      context: __dirname,
    }),
  ],
};

webpack.DllPlugin的选项中,path是manifest文件的输出路径;name是dll暴露的对象名,要跟output.library保持一致;context是解析包路径的上下文,这个要跟接下来配置的dll user一致。

运行Webpack,会输出两个文件一个是打包好的vendor.js,一个就是manifest.json,长这样:

{
  "name": "vendor_ac51ba426d4f259b8b18",
  "content": {
    "./node_modules/antd/dist/antd.js": 1,
    "./node_modules/react/react.js": 2,
    "./node_modules/react/lib/React.js": 3,
    "./node_modules/react/node_modules/object-assign/index.js": 4,
    "./node_modules/react/lib/ReactChildren.js": 5,
    "./node_modules/react/lib/PooledClass.js": 6,
    "./node_modules/react/lib/reactProdInvariant.js": 7,
    "./node_modules/fbjs/lib/invariant.js": 8,
    "./node_modules/react/lib/ReactElement.js": 9,
    
    ............

Webpack将每一个库都进行了编号索引,以后的dll user能够读取这个文件,直接用id来引用。

Dll user的配置:

const webpack = require('webpack');

module.exports = {
  output: {
    path: 'build',
    filename: '[name].[chunkhash].js',
  },
  entry: {
    app: './src/index.js',
  },
  plugins: [
    new webpack.DllReferencePlugin({
      context: __dirname,
      manifest: require('./manifest.json'),
    }),
  ],
};

DllReferencePlugin的选项中,context须要跟以前保持一致,这个用来指导Webpack匹配manifest中库的路径;manifest用来引入刚才输出的manifest文件。

运行Webpack以后,结果以下:

分离出dll后打包

对比一下不作分离的状况下打包的结果:

不分离的打包

速度快了,文件也小了。

平时开发的时候,修改代码后从新编译的速度会大大减小,节省时间。

若是有多个项目,使用相同的一套库,你能够在打包的时候引用相同的manifest文件,这样就能够在项目之间共享了。

参考: