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 相关文章推荐
jQuery formValidator表单验证
Jan 07 Javascript
基于BootStrap Metronic开发框架经验小结【二】列表分页处理和插件JSTree的使用
May 12 Javascript
jquery ajax结合thinkphp的getjson实现跨域的方法
Jun 06 Javascript
JS+HTML5手机开发之滚动和惯性缓动实现方法分析
Jun 12 Javascript
javascript实现滚动效果的数字时钟实例
Jul 21 Javascript
jQuery extend()详解及简单实例
May 06 jQuery
Vue实现左右菜单联动实现代码
Aug 12 Javascript
PM2自动部署代码步骤流程总结
Dec 10 Javascript
vue组件文档(.md)中如何自动导入示例(.vue)详解
Jan 25 Javascript
详解vue 中 scoped 样式作用域的规则
Sep 14 Javascript
JavaScript实现简易计算器小功能
Oct 22 Javascript
vue使用exif获取图片经纬度的示例代码
Dec 11 Vue.js
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
推荐几款用 Sublime Text 开发 Laravel 所用到的插件
2014/10/30 PHP
CI框架装载器Loader.php源码分析
2014/11/04 PHP
Yii开启片段缓存的方法
2016/03/28 PHP
在laravel框架中使用model层的方法
2019/10/08 PHP
XHTML-Strict 内允许出现的标签
2006/12/11 Javascript
基于jquery的划词搜索实现(备忘)
2010/09/14 Javascript
javascript同页面多次调用弹出层具体实例代码
2013/08/16 Javascript
js jquery ajax的几种用法总结(及优缺点介绍)
2014/01/28 Javascript
在jquery中的ajax方法怎样通过JSONP进行远程调用
2014/04/04 Javascript
Node.js的特点和应用场景介绍
2014/11/04 Javascript
JavaScript中函数(Function)的apply与call理解
2015/07/08 Javascript
浅谈使用MVC模式进行JavaScript程序开发
2015/11/10 Javascript
jQuery设置单选按钮radio选中/不可用的实例代码
2016/06/24 Javascript
jQuery实现简易的输入框字数计数功能示例
2017/01/16 Javascript
JS实现发送短信验证后按钮倒计时功能(防止刷新倒计时失效)
2017/07/07 Javascript
ES6正则表达式扩展笔记
2017/07/25 Javascript
AngularJs 终极购物车(实例讲解)
2017/11/08 Javascript
Vue组件全局注册实现警告框的实例详解
2018/06/11 Javascript
ES6基础之解构赋值(destructuring assignment)
2019/02/21 Javascript
借助云开发实现小程序短信验证码的发送
2020/01/06 Javascript
ES5新增数组的实现方法
2020/05/12 Javascript
Python中利用Scipy包的SIFT方法进行图片识别的实例教程
2016/06/03 Python
Python数据类型之List列表实例详解
2019/05/08 Python
python代理工具mitmproxy使用指南
2019/07/04 Python
澳大利亚免息网上购物:Shop Zero
2016/09/17 全球购物
佳能加拿大网上商店:Canon eStore Canada
2018/04/04 全球购物
如何手工释放资源
2013/12/15 面试题
生物技术毕业生自荐信
2013/10/23 职场文书
商超业务员岗位职责
2014/03/12 职场文书
企业优秀员工事迹材料
2014/05/28 职场文书
地球上的星星观后感
2015/06/02 职场文书
大学毕业典礼致辞
2015/07/29 职场文书
农村房屋租赁合同(范本)
2019/07/23 职场文书
无线电通信名词解释
2022/02/18 无线电
Python+Selenium自动化环境搭建与操作基础详解
2022/03/13 Python
Redis超详细讲解高可用主从复制基础与哨兵模式方案
2022/04/07 Redis