详解如何webpack使用DllPlugin


Posted in Javascript onSeptember 30, 2018

前言

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

什么是DLL

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

举个例子:很多产品都用到螺丝,但是工厂在生产不同产品时,不需要每次连带着把螺丝也生产出来,因为螺丝可以单独生产,并给多种产品使用。在这里螺丝的作用就可以理解为是dll。

为什么要使用Dll

通常来说,我们的代码都可以至少简单区分成业务代码和第三方库。如果不做处理,每次构建时都需要把所有的代码重新构建一次,耗费大量的时间。然后大部分情况下,很多第三方库的代码并不会发生变更(除非是版本升级),这时就可以用到dll:把复用性较高的第三方模块打包到动态链接库中,在不升级这些库的情况下,动态库不需要重新打包,每次构建只重新打包业务代码。

还是上面的例子:把每次构建,当做是生产产品的过程,我们把生产螺丝的过程先提取出来,之后我们不管调整产品的功能或者设计(对应于业务代码变更),都不必重复生产螺丝(第三方模块不需要重复打包);除非是产品要使用新型号的螺丝(第三方模块需要升级),才需要去重新生产新的螺丝,然后接下来又可以专注于调整产品本身。

基本用法

使用dll时,可以把构建过程分成dll构建过程和主构建过程(实质也就是如此),所以需要两个构建配置文件,例如叫做webpack.config.jswebpack.dll.config.js

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

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

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,然后到指定的输出文件夹查看输出:

  • react.dll文件里是使用数组保存的模块,索引值就作为id;
  • 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 他们.

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 中保持一致。

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插件进行性能分析)

详解如何webpack使用DllPlugin

详解如何webpack使用DllPlugin

详解如何webpack使用DllPlugin

总结

本文介绍了Dllplugin的思想,基本用法和应用场景(关于使用的部分更详细的内容可以看官方文档),结合个人的一些实践经验,对于常见第三方库是否要放进dll可能比较明确(比如react系列打包一般肯定不亏),但是还有一些就要结合具体的项目内容来判断,例如我上面的实践的例子就说明目前的拆分还不够好。这一块也欢迎大家一起探讨。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JS解决url传值出现中文乱码的另类办法
Apr 08 Javascript
JavaScript打印iframe内容示例代码
Aug 20 Javascript
Jquery中children与find之间的区别详细解析
Nov 29 Javascript
js实现类似菜单风格的TAB选项卡效果代码
Aug 28 Javascript
js图片轮播手动切换效果
Nov 10 Javascript
JS排序之快速排序详解
Apr 08 Javascript
JS实现去除数组中重复json的方法示例
Dec 21 Javascript
vue-prop父组件向子组件进行传值的方法
Mar 01 Javascript
JS/jQuery实现获取时间的方法及常用类完整示例
Mar 07 jQuery
详解Js里的for…in和for…of的用法
Mar 28 Javascript
Vue实现日历小插件
Jun 26 Javascript
JS实现选项卡插件的两种写法(jQuery和class)
Dec 30 jQuery
浅谈angular2子组件的事件传递(任意组件事件传递)
Sep 30 #Javascript
vue-router 手势滑动触发返回功能
Sep 30 #Javascript
Vue CLI3 开启gzip压缩文件的方式
Sep 30 #Javascript
JS数组实现分类统计实例代码
Sep 30 #Javascript
浅谈微信页面入口文件被缓存解决方案
Sep 29 #Javascript
vue实现弹框遮罩点击其他区域弹框关闭及v-if与v-show的区别介绍
Sep 29 #Javascript
vue使用v-for实现hover点击效果
Sep 29 #Javascript
You might like
php 无法加载mcrypt.dll的解决办法
2013/04/03 PHP
PHP获取时间排除周六、周日的两个方法
2014/06/30 PHP
PHP中if和or运行效率对比
2014/12/12 PHP
php 5.6版本中编写一个PHP扩展的简单示例
2015/01/20 PHP
浅析PHP关键词替换的类(避免重复替换,保留与还原原始链接)
2015/09/22 PHP
php技巧小结【推荐】
2017/01/19 PHP
在TP5数据库中四个字段实现无限分类的示例
2019/10/18 PHP
PHP实用小技巧之调用录像的方法
2019/12/05 PHP
用js实现的页面关键字密度查询代码
2007/12/27 Javascript
经典海量jQuery插件 大家可以收藏一下
2010/02/07 Javascript
node.js中的path.dirname方法使用说明
2014/12/09 Javascript
用AngularJS的指令实现tabs切换效果
2016/08/31 Javascript
微信小程序实现点击按钮修改view标签背景颜色功能示例【附demo源码下载】
2017/12/06 Javascript
vue-cli3.0配置及使用注意事项详解
2018/09/05 Javascript
javascript将扁平的数据转为树形结构的高效率算法
2020/02/27 Javascript
微信小程序实现分页加载效果
2020/11/19 Javascript
Python简单日志处理类分享
2015/02/14 Python
玩转python爬虫之正则表达式
2016/02/17 Python
matplotlib在python上绘制3D散点图实例详解
2017/12/09 Python
django模板语法学习之include示例详解
2017/12/17 Python
python+pandas生成指定日期和重采样的方法
2018/04/11 Python
Java文件与类动手动脑实例详解
2019/11/10 Python
pytorch 求网络模型参数实例
2019/12/30 Python
python GUI库图形界面开发之PyQt5计数器控件QSpinBox详细使用方法与实例
2020/02/28 Python
使用python处理题库表格并转化为word形式的实现
2020/04/14 Python
Python requests接口测试实现代码
2020/09/08 Python
matplotlib之多边形选区(PolygonSelector)的使用
2021/02/24 Python
如何进行Linux分区优化
2013/02/12 面试题
JAVA程序设计笔试题面试题一套
2015/07/28 面试题
2013年办公室秘书的个人自我鉴定
2013/10/24 职场文书
部门群众路线教育实践活动对照检查材料思想汇报
2014/10/07 职场文书
个人融资协议书范本两则
2014/10/15 职场文书
监察建议书
2015/02/04 职场文书
工作保证书怎么写
2015/02/28 职场文书
2015年八一建军节慰问信
2015/03/23 职场文书
立案决定书范文
2015/06/24 职场文书