IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    Webpack的dll功能

    王成发表于 2016-07-15 00:20:01
    love 0

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

    我们构建前端项目的时候,往往希望第三方库(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也变化了!

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

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

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

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

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

    打包dll的时候,Webpack会将所有包含的库做一个索引,写在一个manifest文件中,而引用dll的代码(dll user)在打包的时候,只需要读取这个manifest文件,就可以了。

    这么一来有几个好处:

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

    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文件,这样就可以在项目之间共享了。

    参考:

    • https://webpack.github.io/docs/list-of-plugins.html#dllplugin

    • https://github.com/webpack/webpack/tree/master/examples/dll



沪ICP备19023445号-2号
友情链接