這篇文章討論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。