提高webpack构建效率之DLLPlugin

Is life always this hard, or is it just when you're a kid?

1.前言

webpack是一个将前端资源模块化打包的工具。借助该工具,我们在项目开发的过程中,可以更便宜的处理模块间的依赖关系。而commonjs的模块和ES6的处理代码必须在浏览器使用前,经过webpack的构建。

模块会随着项目的进展而逐渐增多,随之webpack打包的时间也会加长。最近开发的项目,经过基本的优化,打包的时间已经超过120s, 而改动代码后,至少也要10s页面才能重新刷新。如此,开阿发效率大大降低。

2. CommonsChunkPlugin

分析了下(这儿可以贴张图),主要占打包资源的是依赖的Reactaxiossemantic-react-ui等等引入的包依赖,在执行node webpack.config.js的时候,所有的依赖都会重新打包一遍。之前我采用的是CommonsChunkPlugin来对相同的模块,单独提取出来打包,进而减小rebuild的性能。

var config = {
  // 入口文件
  entry: {
    app: ['./index.js'],
    lib: [
       'react', 'react-dom', 'react-router',
       'redux', 'react-redux', 'redux-thunk'
     ],
  },
  output: {
    path: './',
    filename: '[name].js',
  },
  plugins: [
    // 提取共同模块
    new webpack.optimize.CommonsChunkPlugin({
       names: ['lib', 'app']
    }),
  ]
}

然而CommonsChunkPlugin只在rebuild的时候有一点效果,初次运行的时间仍然没有变。

网上搜索加上实际操作,终于让我找到了解决方法。

3. DLL & DllReference

DLL

DLL 插件不执行任何模块的代码,只是引入模块。最终导出一个可以用于通过id(内部引入函数)来引入模块的函数和一个被写入特殊位置的manifest.json文件,该文件包含实际请求到模块id的映射。

详细参考 dllplugin

该插件结合output.library选项,使dll方法可以在全局范围内使用。

webpack.dll.js

new DllPlugin({
  path: path.join(__dirname, "manifest.json"),
  name: "[name]_[hash]",
  context: __dirname
})
  • path : manifest文件生成的位置,绝对路径
  • name : dll 方法的名称(与 output.library 保持一致)
  • context (可选) : manifest文件中请求(对模块的引入请求)的上下文环境,默认与webpack环境保持一致

DllReference

用于引用可用的dll函数,可通过manifest 文件将模块名称映射到模块的 id
该插件可用于配置 DllPlugin创建的 dll包和 Manifest 文件。并且可以通过以下两种方式访问:

作用域模式

dll 的内容可以通过一个模块的前缀访问。例如:假设 scope="xyz", 在 dll 中的 abc 文件就可以通过 require("xyz/abc") 来访问。

映射模式

dll 的内容被映射到当前路径。如果通过 require 引入的文件名和dll中文件名(解析后)相匹配,那么将使用dll中的文件来。注意:由于这个过程是发生在解析后,所以dll用户必须可以访问到同一路径下的所有dll文件。例如:如果dll文件包含 jquery 和文件 abc, require("jquery")requier("./abc") 将从dll文件中去获取。

详细参考 dllreferenceplugin

webpack.config.js

new DllReferencePlugin({
  context: __dirname,
  scope: "xyz",
  manifest: require("./manifest.json"),
  name: "./my-dll.js",
  sourceType: "commonsjs2",
  content: { ... }
})
  • context: (absolute path) context of requests in the manifest (or content property)
  • scope (optional): prefix which is used for accessing the content of the dll
    manifest (object): an object containing content and name
  • name (optional): the name where the dll is exposed (defaults to manifest.name) (see also externals)
  • sourceType (optional): the type how the dll is exposed (defaults to “var”) (see also externals)
  • content (optional): the mappings from request to module id (defaults to manifest.content)

4. 实际操作

  1. 在根目录单独建立一个 webpack.dll.js

    var webpack = require('webpack')
    
    module.exports = {
        output: {
            path: './dll',
            filename: '[name].js',
            /**
             * output.library
             * 将会定义为 window.${output.library}
             * 在这次的例子中,将会定义为`window.vendor_library`
             */
            library: '[name]_library',
            libraryTarget: 'commonjs2'
        },
        entry: {
            // 入口,依赖的外部模块
            vendor: [
              'react', 'react-dom', 'react-router', 'redux', 'react-redux', 
              'redux-thunk', 'semantic-ui-react', 'lodash', 'echarts', 'axios'
            ]
        },
        plugins: [
            new webpack.DllPlugin({
               /**
                * path
                * 定义 manifest 文件生成的位置
                * [name]的部分由entry的名字替换
                */
                path: './dll/[name]-manifest.json',
               /**
                * name
                * dll bundle 输出到那个全局变量上
                * 和 output.library 一样即可。 
                */
                name: '[name]',
                context: __dirname
            })
        ]
    }
    
  1. 生成dll文件

    执行./node_modules/.bin/webpack --config build/webpack.config.js

    请在每次依赖的模块更改时重新执行这个命令

  2. index.html中引入dll文件

    这一步必不可少,之前因为少了这一步,一直报 vendor_library not defined

    //这个模块的引入必须在自定义文件的入口之前
    <script src="dist/dll/vendor.dll.js"></script>
    
  3. webpack.config.js

    var webpack = require('webpack')
    
    module.exports = {
        output: {
            path: __dirname,
            filename: '[name].bundle.js',
        },
        entry: {
            app: ['./app.js']
        },
        plugins: [
            new webpack.DllReferencePlugin({
                context: __dirname,
                manifest: require('./dll/test-manifest.json'),
                sourceType: 'commonjs2',
                name: './dll/test.js'
            })
        ]
    }
    

BugFixed

配置中常可能会出现的问题:

  1. vendor not defined : 模块没有在html中引入,在根目录文件夹中加入script标签引入dll文件。

  2. 打包时间没有变快: 检查下webpack.config.jscontext是否和webpack.config.js的一致,不一致的话保持一致,一般这个context的默认值是开发项目的根路径。

参考

  1. Webpack Plugins we been keepin on the DLL
Share Comments