先不進源碼,分析一下打包后的文件,來一張圖:
首先創建兩個JS文件,內容如下:
// config.js module.exports = { entry: './input.js', output: { filename: 'output.js' } }
// input.js console.log('input')
分別為配置文件和入口JS文件,內容弄個簡單的。
接下來在當前目錄執行webpack --config config.js,會輸出一個output.js,簡化后內容如下:
(function(modules) { // webpackBootstrap // 模塊緩存對象 var installedModules = {}; function __webpack_require__(moduleId) { // 加載入口JS // 輸出 return module.exports; } // 掛載模塊數組 __webpack_require__.m = modules; // ... // 在__webpack_require__掛載多個屬性 // 傳入入口JS模塊ID執行函數並輸出模塊 return __webpack_require__(__webpack_require__.s = 0); }); // 包含所有模塊的數組 ([ /* id為0 */ (function(module, exports) { console.log('1') }) ]);
可以看到,這是一個IIFE,可以利用閉包來對模塊進行緩存以及其余便利性的功能。
整個JS可以分為三塊:
1、傳入包含所有模塊的數組,每一個模塊有唯一的標識ID
2、模塊輸出函數
3、在函數上掛載多個屬性
// 傳入入口文件的模塊ID function __webpack_require__(moduleId) { // 檢測緩存 if (installedModules[moduleId]) { return installedModules[moduleId].exports; } // 無緩存時創建新的模塊 // i => 模塊ID // l => 是否被加載 // exports => 輸出內容 var module = installedModules[moduleId] = { i: moduleId, l: false, exports: {} }; // 執行模塊函數 modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); // 標記模塊已被加載 module.l = true; // 返回模塊輸出 return module.exports; }
入口文件的模塊ID會當成參數傳入該函數,首先會進行緩存檢測,沒找到會生成一個新的模塊對象,然后執行入口模塊,將該模塊標記為已加載后返回對應的exports。
由於被打包的文件內容十分簡單,所以只會執行console語句。
由於函數也是對象,所以可以在上面添加屬性,具體代碼如下:
// 模塊數組 __webpack_require__.m = modules; // 模塊緩存 __webpack_require__.c = installedModules; // 屬性判斷 __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; // 如果不存在對應屬性 定義輸出的getter __webpack_require__.d = function(exports, name, getter) { if (!__webpack_require__.o(exports, name)) { Object.defineProperty(exports, name, { configurable: false, enumerable: true, get: getter }); } }; // 默認模塊 __webpack_require__.n = function(module) { var getter = module && module.__esModule ? function getDefault() { return module['default']; } : function getModuleExports() { return module; }; __webpack_require__.d(getter, 'a', getter); return getter; }; // __webpack_public_path__ __webpack_require__.p = "";
掛載了6個屬性,模塊組、緩存對象、屬性檢測方法、getter定義、默認模塊、公共路徑.
這里的getter稍微解釋一下,module的引入模塊有兩種輸出模式,一種是webpack定義的module.exports,另一種是ES定義的export default,這里就是判斷是哪一種方式。默認情況下當然是module.exports獲取模塊輸出內容。但是用ES的方式,模塊輸出會被包裹在一個default對象,此時需要用module['default']來獲取。
接下來弄復雜一些再觀察,配置文件不變,改變入口JS並添加一個依賴JS:
// require,js module.exports = function require() { console.log('require'); }
// input.js import rq from "./require.js" rq();
簡單的實現一個JS加載另外一個JS,接下來執行打包。
打包后主處理函數不會變,傳入的模塊數組發生了變化,代碼如下:
(function(modules) { // ... return __webpack_require__(__webpack_require__.s = 0); }) ([ /* 0 */ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; Object.defineProperty(__webpack_exports__, "__esModule", { value: true }); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__require_js__ = __webpack_require__(1); /* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__require_js___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0__require_js__); __WEBPACK_IMPORTED_MODULE_0__require_js___default()() }), /* 1 */ (function(module, exports) { module.exports = function require() { console.log('require'); } }) ]);
這里的模塊數組出現了2個,但是入口的JS仍然只有1個,ID為0,被依賴的ID為1。
可以注意到,module、exports分別對應函數的2個參數,而在主處理函數中exports就是輸出的模塊內容,所以這就是為什么打包函數都是通過module.exports來輸出模塊內容。
這里仔細看一下ID為0的入口模塊:
/* 0 */ (function(module, __webpack_exports__, __webpack_require__) { "use strict"; // 定義該模塊的exports的__esModule屬性為true // 該屬性影響__webpack_require__.n的輸出 Object.defineProperty(__webpack_exports__, "__esModule", { value: true }); // 獲取ID為1的模塊 var __WEBPACK_IMPORTED_MODULE_0__require_js__ = __webpack_require__(1); // 這里傳入了模塊1並調用了__webpack_require__.n // 模塊1並沒有被標記__esModule // 返回函數function getModuleExports() { return module; }; var __WEBPACK_IMPORTED_MODULE_0__require_js___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0__require_js__); // 注意這里的module並不是模塊的module對象 而是傳入函數n的參數 // 所以這里的調用相當於__webpack_require__(1)() => (function require() {console.log('require');})() __WEBPACK_IMPORTED_MODULE_0__require_js___default()() }),
由於案例比較簡單,所以雖然變量名很長,但是也非常好懂,引入依賴JS后,判斷模塊的輸出模式,然后執行輸出代碼。
暫時先寫這么多。