webpack使用-详解DllPlugin

前言

(时光飞逝,转眼又偷懒了一个多月)node

什么是DLL

DLL(Dynamic Link Library)文件为动态连接库文件,在Windows中,许多应用程序并非一个完整的可执行文件,它们被分割成一些相对独立的动态连接库,即DLL文件,放置于系统中。当咱们执行某一个程序时,相应的DLL文件就会被调用。

举个例子:不少产品都用到螺丝,可是工厂在生产不一样产品时,不须要每次连带着把螺丝也生产出来,由于螺丝能够单独生产,并给多种产品使用。在这里螺丝的做用就能够理解为是dll。react

为何要使用Dll

一般来讲,咱们的代码均可以致少简单区分红业务代码第三方库。若是不作处理,每次构建时都须要把全部的代码从新构建一次,耗费大量的时间。而后大部分状况下,不少第三方库的代码并不会发生变动(除非是版本升级),这时就能够用到dll:把复用性较高的第三方模块打包到动态连接库中,在不升级这些库的状况下,动态库不须要从新打包,每次构建只从新打包业务代码webpack

仍是上面的例子:把每次构建,当作是生产产品的过程,咱们把生产螺丝的过程先提取出来,以后咱们无论调整产品的功能或者设计(对应于业务代码变动),都没必要重复生产螺丝(第三方模块不须要重复打包);除非是产品要使用新型号的螺丝(第三方模块须要升级),才须要去从新生产新的螺丝,而后接下来又能够专一于调整产品自己。web

基本用法

使用dll时,能够把构建过程分红dll构建过程和主构建过程(实质也就是如此),因此须要两个构建配置文件,例如叫作webpack.config.jswebpack.dll.config.jsjson

1. 使用DLLPlugin打包须要分离到动态库的模块

DllPluginwebpack内置的插件,不须要额外安装,直接配置webpack.dll.config.js文件:redux

module.exports = {=
  entry: {
    // 第三方库
    react: ['react', 'react-dom', 'react-redux']
  },
  output: {
    // 输出的动态连接库的文件名称,[name] 表明当前动态连接库的名称,
    filename: '[name].dll.js',
    path: resolve('dist/dll'),
    // library必须和后面dllplugin中的name一致 后面会说明
    library: '[name]_dll_[hash]'
  },
  plugins: [
  // 接入 DllPlugin
    new webpack.DllPlugin({
      // 动态连接库的全局变量名称,须要和 output.library 中保持一致
      // 该字段的值也就是输出的 manifest.json 文件 中 name 字段的值
      name: '[name]_dll_[hash]',
      // 描述动态连接库的 manifest.json 文件输出时的文件名称
      path: path.join(__dirname, 'dist/dll', '[name].manifest.json')
    }),
  ]
}

咱们先来看看,这一步到底作了什么。执行:webpack --config webpack.dll.config,而后到指定的输出文件夹查看输出:数组

  1. react.dll文件里是使用数组保存的模块,索引值就做为id;
  2. react.manifest.json文件里,是用来描述对应的dll文件里保存的模块

里暴露出刚刚构建的全部模块,以下:浏览器

{
  "name":"react_dll_553e24e2c44987d2578f",
  "content":{
    "./node_modules/webpack/node_modules/process/browser.js":{"id":0,"meta":{}},"./node_modules/react/node_modules/fbjs/lib/invariant.js":{"id":1,"meta":{}},"./node_modules/react/lib/Object.assign.js":{"id":2,"meta":{}},"./node_modules/react/node_modules/fbjs/lib/warning.js":{"id":3,"meta":{}}
    //省略类似代码
  }
}

2. 在主构建配置文件使用动态库文件

webpack.config中使用dll要用到DllReferencePlugin,这个插件经过引用 dll 的 manifest 文件来把依赖的名称映射到模块的 id 上,以后再在须要的时候经过内置的 webpack_require 函数来 require 他们.app

new webpack.DllReferencePlugin({
    context: __dirname,
    manifest: require('./dist/dll/react.manifest.json')
  }),

第一步产出的manifest文件就用在这里,给主构建流程做为查找dll的依据:DllReferencePlugin去 manifest.json 文件读取 name 字段的值,把值的内容做为在从全局变量中获取动态连接库中内容时的全局变量名,所以:在 webpack_dll.config.js 文件中,DllPlugin 中的 name 参数必须和 output.library 中保持一致。dom

3. 在入口文件引入dll文件。

生成的dll暴露出的是全局函数,所以还须要在入口文件里面引入对应的dll文件。

<body>
  <div id="app"></div>
  <!--引用dll文件-->
  <script src="../../dist/dll/react.dll.js" ></script>
</body>

做用

首先从前面的介绍,至少能够看出dll的两个做用

  1. 分离代码,业务代码和第三方模块能够被打包到不一样的文件里,这个有几个好处:

    • 避免打包出单个文件的大小太大,不利于调试
    • 将单个大文件拆成多个小文件以后,必定状况下有利于加载(不超出浏览器一次性请求的文件数状况下,并行下载确定比串行快)
  2. 提高构建速度。第三方库没有变动时,因为咱们只构建业务相关代码,相比所有从新构建天然要快的多。

注意事项

从前面能够看到dll带来的优势,但并不意味着咱们就应该把除业务代码外的全部代码所有都丢到dll中,举一个例子:
1.对于lodash这种第三方库,正确的用法是只去import所需的函数(用什么引什么),例如:

// 正确用法
import isPlainObject from 'lodash/isPlainObject'

//错误用法
import { isPlainObject } from 'lodash'

这两种写法的差异在于,打包时webpack会根据引用去打包依赖的内容,因此第一种写法,webpack只会打包lodash的isPlainObject库,第二种写法却会打包整个lodash。如今假设在项目中只是用到不一样模块对lodash里的某几个函数而且没有对于某个函数重复使用很是屡次,那么这时候把lodash添加到dll中,带来的收益就并不明显,反而致使2个问题:

  1. 因为打包了整个lodash,而致使打包后的文件总大小(注意是总大小)比原先还要大
  2. 在dll打包太多内容也须要耗费时间,虽然咱们通常只在第三方模块更新以后才进行从新预编译(就是dll打包的过程),可是若是这个时间太长的话体验也很差、

实践与反思

放一张本身在一个比较大的项目中单纯使用dll以后的收益,提取的内容是 react相关的第三方库,和fish组件,构建时间从120s下降到80s左右(固然这个时间仍是有点恐怖),构建前appjs的大小是680kb,拆分业务代码和第三方代码分别是400kb和380kb(这就是拆分后大小大于拆分前大小的例子),从这一点来看,对于常见第三方库是否要放进dll可能比较明确(好比react系列打包通常确定不亏),可是还有一些就要结合具体的项目内容来进行判断和取舍。(强烈推荐使用webpack-bundle-analyzer插件进行性能分析)
图片描述
图片描述
图片描述

总结

本文介绍了Dllplugin的思想,基本用法和应用场景(关于使用的部分更详细的内容能够看官方文档),结合我的的一些实践经验,对于常见第三方库是否要放进dll可能比较明确(好比react系列打包通常确定不亏),可是还有一些就要结合具体的项目内容来判断,例如我上面的实践的例子就说明目前的拆分还不够好。这一块也欢迎你们一块儿探讨。若是内容有错误的地方欢迎指出(以为看着不理解不舒服想吐槽也彻底没问题);若是对你有帮助,欢迎点赞和收藏,转载请征得赞成后著明出处,若是有问题也欢迎私信交流,主页有邮箱地址

相关文章
相关标签/搜索