这篇文章讨论Webpack打包library时常常须要用到的一个选项external
,它用于避免将一些很通用的模块打包进你发布的library里,而是选择把它们声明成external的模块,在你的library被上层使用后,在最后阶段由Webpack统一把这个external的依赖模块打包进来。javascript
external
选项通常都是用在打包library上面,若是不是library而是一个最终的app的发布JS文件,那external也没有什么意义。关于Webpack打包library的分析和一些选项的做用,我在前一篇文章作了讨论。java
external
选项咱们仍然使用前一篇文章的例子,定义一个库util.js
:node
import $ from 'jquery' function hideImages() { $('img').hide(); } export default { "hideImages": hideImages }
咱们使用Webpack打包发布这个库:jquery
// 入口文件 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的复制,固然这可能并不会影响它的正常功能,可是会占据很大的代码体积。webpack
因此一般状况下当你的库须要依赖到例如jquery
,bootstrap
这样的通用JS模块时,咱们能够不将它打包进bundle,而是在Webpack的配置中声明external
:web
externals: { jquery: { root: 'jquery', commonjs: 'jquery', commonjs2: 'jquery', amd: 'jquery', }, },
这就是在告诉Webpack:请不要将这个模块注入编译后的JS文件里,对于我源代码里出现的任何import/require
这个模块的语句,请将它保留。npm
咱们能够看一下编译后的bundle文件的结构:json
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
相关的语句也被保留了原意:bootstrap
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
。segmentfault
对于external的依赖模块,一般你能够这样作,例如你使用npm发布你的库,你能够将jquery在package.json
文件中添加到dependencies
,这样别人npm install
你发布的库时,jquery也会被自动下载到node_modules供别人打包使用。
若是咱们使用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的参数传入。对于每种运行环境,各有各的作法:
require('jquery')
语句。define
中将jquery
定义为依赖模块。jquery
变量,这须要jquery在该模块以前就已经被加载。而后不论是哪一种状况,它们都将载入后的jquery
模块做为参数传入factory
函数,这样就能正确加载util
模块了。
以上涉及到Webpack生成代码的部分可能有点绕,须要你比较了解Webpack打包模块的机制和原理,关于这部分我在这篇文章里作了详细讨论。
以上就是关于Webpack的external
选项的使用,而且从编译后的JS代码分析了它究竟是如何起做用的。我想阅读Webpack相关的生成代码仍是很重要的,这样才算是真正地理解了external的机制,在碰到一些坑时才能知道怎么去debug。