webpack external模块的具体使用


Posted in Javascript onMarch 10, 2018

这篇文章讨论Webpack打包library时经常需要用到的一个选项external,它用于避免将一些很通用的模块打包进你发布的library里,而是选择把它们声明成external的模块,在你的library被上层使用后,在最后阶段由Webpack统一把这个external的依赖模块打包进来。

external选项一般都是用在打包library上面,如果不是library而是一个最终的app的发布JS文件,那external也没有什么意义。关于Webpack打包library的分析和一些选项的作用,我在前一篇文章做了讨论。

external选项

我们仍然使用前一篇文章的例子,定义一个库util.js:

import $ from 'jquery'

function hideImages() {
 $('img').hide();
}

export default {
 "hideImages": hideImages
}

我们使用Webpack打包发布这个库:

// 入口文件
entry: {
 util: './util.js',
}

// 输出文件
output: {
 path: './dist',
 filename: '[name].dist.js'

 library: 'util',
 libraryTarget: commonjs2,
 targetExport: 'default'
}

这样打包出来的util.dist.js文件会把jquery的代码完整地注入进去,因为你的源代码使用到了它。但是这往往并不是我们希望的,因为jquery是很通用的模块,在一个app中,很可能其它的库也会用到它,最顶层的入口文件app也可能用到它,如果每一个库模块的发布版本都将jquery原封不动地打包进了自己的bundle,最后拼到一起,在最终的app发布代码里就会有很多份jquery的复制,当然这可能并不会影响它的正常功能,但是会占据很大的代码体积。

所以通常情况下当你的库需要依赖到例如jquery,bootstrap这样的通用JS模块时,我们可以不将它打包进bundle,而是在Webpack的配置中声明external:

externals: {
 jquery: {
  root: 'jquery',
  commonjs: 'jquery',
  commonjs2: 'jquery',
  amd: 'jquery',
 },
},

这就是在告诉Webpack:请不要将这个模块注入编译后的JS文件里,对于我源代码里出现的任何import/require这个模块的语句,请将它保留。

我们可以看一下编译后的bundle文件的结构:

module.exports = (function(modules) {
 var installedModules = {};
 function webpack_require(moduleId) {
   // ...
 }
 return webpack_require('./util.js');
}) ({
 './util.js': generated_util,
 // '/path/to/jquery.js': generated_jquery 原本有这一行,现在被删去。
});

可以看到jquery模块没有被打包进bundle文件,而对于util,它的生成代码即generated_util函数中关于import jquery相关的语句也被保留了原意:

function generated_util(module, exports, webpack_require) {
 var $ = require('jquery');
 // util的其它源代码
 // ...
}

当然也并非完全没有修改,例如将import的改回了传统的require关键词,因为我们这里用的是CommonJS风格的打包方式。不过这些都是次要的,关键是它保留了require这个关键词,而没有使用webpack_require将jquery真的引入进来。这就是说,当前的这个JS文件的模块管理系统中是没有jquery的,它是一个external的模块,需要在这个JS文件被其它人引用并且在上层编译时,jquery才可能被真的引入进来,到那个时候这里的require关键词才会被替换为webpack_require。

对于external的依赖模块,通常你可以这样做,例如你使用npm发布你的库,你可以将jquery在package.json文件中添加到dependencies,这样别人npm install你发布的库时,jquery也会被自动下载到node_modules供别人打包使用。

umd格式下的打包

如果我们使用umd格式打包,我们可以看到在不同环境中,external模块是如何发挥作用的:

(function webpackUniversalModuleDefinition(root, factory) {
 if(typeof exports === 'object' && typeof module === 'object') // commonjs2
  module.exports = factory(require('jquery'));
 else if(typeof define === 'function' && define.amd)
  define("util", ['jquery'], factory); // amd
 else if(typeof exports === 'object')
  exports["util"] = factory(require('jquery')); // commonjs
 else
  root["util"] = factory(root['jquery']); // var
}) (window, function(__webpack_external_module_jquery__) {
 return (function(modules) {
  var installedModules = {};
  function webpack_require(moduleId) {
    // ...
  }
  return webpack_require('./util.js');
 }) ({
  './util.js': generated_util,
 });
}

而generated_util也相应地增加一个参数__webpack_external_module_jquery__:

function generated_util(module, exports, webpack_require,
            __webpack_external_module_jquery__) {
 var $ = __webpack_external_module_jquery__;
 // util的其它源代码
 // ...
}

这样的写法似乎结构和上面的CommonJS的编译版本不太一样,但实际上本质是一样的。因为现在umd要照顾到不同的运行环境,所以它把require('jquery')提前了,作为factory的参数传入。对于每种运行环境,各有各的做法:

  1. CommonJS:保留require('jquery')语句。
  2. AMD:在define中将jquery定义为依赖模块。
  3. Var:从全局域中取出jquery变量,这需要jquery在该模块之前就已经被加载。

然后不管是哪种情况,它们都将载入后的jquery模块作为参数传入factory函数,这样就能正确加载util模块了。

以上涉及到Webpack生成代码的部分可能有点绕,需要你比较了解Webpack打包模块的机制和原理,关于这部分我在这篇文章里做了详细讨论。

总结

以上就是关于Webpack的external选项的使用,并且从编译后的JS代码分析了它到底是如何起作用的。我想阅读Webpack相关的生成代码还是很重要的,这样才算是真正地理解了external的机制,在碰到一些坑时才能知道怎么去debug。

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

Javascript 相关文章推荐
ExtJs使用总结(非常详细)
Mar 22 Javascript
javascript中parentNode,childNodes,children的应用详解
Dec 17 Javascript
类似php的js数组的in_array函数自定义方法
Dec 27 Javascript
jQuery获取复选框被选中数量及判断选择值的方法详解
May 25 Javascript
angular源码学习第一篇 setupModuleLoader方法
Oct 20 Javascript
web 屏蔽BackSpace键实例代码
Dec 24 Javascript
angularJs中$http获取后台数据的实例讲解
Aug 08 Javascript
js jquery 获取某一元素到浏览器顶端的距离实现方法
Sep 05 jQuery
JS 实现微信扫一扫功能
Sep 14 Javascript
Jquery动态列功能完整实例
Aug 30 jQuery
深入浅析golang zap 日志库使用(含文件切割、分级别存储和全局使用等)
Feb 19 Javascript
解决vue自定义指令导致的内存泄漏问题
Aug 04 Javascript
webpack组织模块打包Library的原理及实现
Mar 10 #Javascript
浅谈webpack组织模块的原理
Mar 10 #Javascript
Vuex实现计数器以及列表展示效果
Mar 10 #Javascript
在vue中使用css modules替代scroped的方法
Mar 10 #Javascript
redux-saga 初识和使用
Mar 10 #Javascript
JS获取input[file]的值并显示在页面的实现方法
Mar 09 #Javascript
vue获取当前点击的元素并传值的实例
Mar 09 #Javascript
You might like
Can't create/write to file 'C:\WINDOWS\TEMP\...MYSQL报错解决方法
2011/06/30 PHP
查找mysql字段中固定字符串并替换的几个方法
2012/09/23 PHP
PHP中mysql_field_type()函数用法
2014/11/24 PHP
php+html5+ajax实现上传图片的方法
2016/05/14 PHP
php7 安装yar 生成docker镜像
2017/05/09 PHP
深入研究PHP中的preg_replace和代码执行
2018/08/15 PHP
jquery图片延迟加载 前端开发技能必备系列
2012/06/18 Javascript
javascript 中that的含义示例介绍
2014/05/14 Javascript
JavaScript中使用document.write向页面输出内容实例
2014/10/16 Javascript
JavaScript中扩展Array contains方法实例
2020/08/23 Javascript
jquery+css3实现网页背景花瓣随机飘落特效
2015/08/17 Javascript
Javascript之String对象详解
2016/06/08 Javascript
AngularJs  Understanding Angular Templates
2016/09/02 Javascript
一道面试题引发的对javascript类型转换的思考
2017/03/06 Javascript
Vue 2.0中生命周期与钩子函数的一些理解
2017/05/09 Javascript
解决在vue项目中,发版之后,背景图片报错,路径不对的问题
2018/03/06 Javascript
微信小程序日历效果
2018/12/29 Javascript
Vue项目中使用better-scroll实现菜单映射功能方法
2019/09/11 Javascript
jquery获取并修改触发事件的DOM元素示例【基于target 属性】
2019/10/10 jQuery
Layui数据表格判断编辑输入的值,是否为我需要的类型详解
2019/10/26 Javascript
Python实现针对含中文字符串的截取功能示例
2017/09/22 Python
解决python线程卡死的问题
2019/02/18 Python
python爬虫开发之使用Python爬虫库requests多线程抓取猫眼电影TOP100实例
2020/03/10 Python
详解如何使用CSS3中的结构伪类选择器和伪元素选择器
2020/01/06 HTML / CSS
HTML5中实现拖放效果无须借助javascript
2012/12/26 HTML / CSS
Groupon比利时官方网站:特卖和网上购物高达-70%
2019/08/09 全球购物
托管代码(Managed Code)和非托管代码(Unmanaged Code)有什么区别
2014/09/29 面试题
Ajax的工作原理
2015/12/04 面试题
优秀中专生推荐信
2013/11/17 职场文书
环保建议书作文
2014/03/12 职场文书
《学会合作》教学反思
2014/04/12 职场文书
学风建设演讲稿
2014/09/12 职场文书
设备收款委托书范本
2014/10/02 职场文书
勇敢的心观后感
2015/06/09 职场文书
导游词之重庆渣滓洞
2020/01/08 职场文书
MySQL子查询中order by不生效问题的解决方法
2021/08/02 MySQL