一、一個入口,一個檔案
webpack.config.js
module.exports = {
entry: './main.js', // 一個入口
output: {
filename: 'bundle.js'
}
};
複製程式碼
main.js
document.write('<h1>Hello World</h1>'); // 一個檔案
複製程式碼
bundle.js
/******/ (function(modules) { // webpackBootstrap
/******/ // module快取物件
/******/ var installedModules = {};
/******/
/******/ // require函式
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // 檢查module是否在快取當中,若在,則返回exports物件
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // 若不在,則以moduleId為key建立一個module,並放入快取當中
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // 執行module函式
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // 標誌module已經載入
/******/ module.l = true;
/******/
/******/ // 返回module的匯出模組
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // 暴露modules物件(__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // 暴露module快取
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // 認證和諧匯入模組具有正確的上下文的函式
/******/ __webpack_require__.i = function(value) { return value; };
/******/
/******/ // 為和諧匯入模組定義getter函式
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, {
/******/ configurable: false,
/******/ enumerable: true,
/******/ get: getter
/******/ });
/******/ }
/******/ };
/******/
/******/ // 相容非和諧模組的getDefaultExport函式
/******/ __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;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // 設定webpack公共路徑__webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/ // 讀取入口模組,返回exports匯出
/******/ return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports) { // 模組ID為0
document.write('<h1>Hello World</h1>');
/***/ })
/******/ ]);
複製程式碼
整體分析 整個的bundle.js是一個立即執行函式表示式(IIFE),傳入的引數modules是一個陣列,陣列的每一項都是一個匿名函式,代表一個模組。在這裡,陣列的第一個引數是一個function,裡面的內容就是原先main.js裡面的內容。
IIFE裡面存在一個閉包,_webpack_require__是模組載入函式,作用是宣告對其他模組的依賴,並返回exports。引數為模組的Id(每一個模組都有一個唯一的id),不需要提供模組的相對路徑,可以省掉模組識別符號的解析過程(準確說,webpack是把require模組的解析過程提前到了構建期),從而可以獲得更好的執行效能。 執行modules函式的是:
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
複製程式碼
通過借用call來使函式的this始終為module本身,引數__webpack_require__是為了讓modules有載入其他模組的能力。
程式流程
bundle通過__webpack_require__(webpack_require.s = 0)來啟動整個程式。首先看看快取中有沒有ID為0的模組,若存在則返回快取中的exports暴露出來的物件;若不存在,則新建module物件,並放入快取當中。此時,module物件和該模組的快取物件installedModules[moduleId]還沒有資料,所以要執行該模組來返回具體require其他模組的資料。傳入的context是module.exports(等於installedModules[moduleId].exports)。
二、一個入口,多個檔案
webpack.config.js
module.exports = {
entry: './main1.js',
output: {
filename: 'bundle.js'
}
}
複製程式碼
main1.js
var main = require('./main2.js')
document.write('<h1>Hello World</h1>');
main.webpack();
複製程式碼
main2.js
exports.webpack = function() {
document.write('<h2>Hello Webpack</h2>');
}
複製程式碼
bundle.js
/******/ (function(modules) { // webpackBootstrap
/******/ 這部分和上面的一樣
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 1);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports) {
exports.webpack = function() {
document.write('<h2>Hello Webpack</h2>');
}
/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {
var main = __webpack_require__(0)
document.write('<h1>Hello World</h1>');
main.webpack();
/***/ })
/******/ ]);
複製程式碼
整體分析 webpack在打包的時候會分析模組間的依賴關係,對匯入匯出模組相關的內容做一個替換。比方說在main1.js檔案中,遇到var main = require('./main2.js')就會轉化為var WEBPACK_IMPORTED_MODULE_0__main = webpack_require(0)。 由於有兩個檔案,所以IIFE的引數為長度是2的陣列,並且按照require的順序排列。
程式的流程 bundle通過__webpack_require__(webpack_require.s = 1)來啟動整個程式。與第一種情況不同的是,這裡首先檢查快取中是否有ID為1的模組,因為main1.js依賴於main2.js,所以在main.js中呼叫模組載入函式。當執行到var main = webpack_require(0),會執行module[0].call(這裡的call為了確保每個module中的this指向的是module本身),然後執行document.write('
Hello World
');,最後執行document.write('Hello Webpack
');。至此,webpack_require(1)執行完畢,這是一個遞迴的過程。三、兩個入口,兩個出口檔案
main1包含inner1;main2包含inner1和inner2。
webpack.config.js
module.exports = {
entry: {
bundle1: './main1.js',
bundle2: './main2.js'
},
output: {
filename: '[name].js'
}
};
複製程式碼
main1.js
var inner1 = require('./inner1.js');
inner1.inner1();
document.write("<h1>我是main1</h1>")
複製程式碼
main2.js
var inner1 = require('./inner1.js');
var inner2 = require('./inner2.js');
inner1.inner1();
inner2.inner2();
document.write("<h1>我是main2</h1>");
複製程式碼
bundle1.js
/******/ (function(modules) { // webpackBootstrap
/******/ 這部分和上面的一樣
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 2);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports) {
exports.inner1 = function() {
document.write('<h1>我是inner1</h1>');
}
/***/ }),
/* 1 */,
/* 2 */
/***/ (function(module, exports, __webpack_require__) {
var inner1 = __webpack_require__(0);
inner1.inner1();
document.write("<h1>我是main1</h1>")
/***/ })
/******/ ]);
複製程式碼
bundle2.js
/******/ (function(modules) { // webpackBootstrap
/******/ 這部分和上面的一樣
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 3);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports) {
exports.inner1 = function() {
document.write('<h1>我是inner1</h1>');
}
/***/ }),
/* 1 */
/***/ (function(module, exports) {
exports.inner2 = function() {
document.write('<h1>我是inner2</h1>');
}
/***/ }),
/* 2 */,
/* 3 */
/***/ (function(module, exports, __webpack_require__) {
var inner1 = __webpack_require__(0);
var inner2 = __webpack_require__(1);
inner1.inner1();
inner2.inner2();
document.write("<h1>我是main2</h1>");
/***/ })
/******/ ]);
複製程式碼
整體分析 對於多入口檔案的情況,分別獨立執行單個入口的情況,每個入口檔案互不干擾。 從上面可以看到,兩個入口檔案main1.js和main2.js的module id都是0,所以可以知道每個入口檔案對應的module id都是0。又因為每個module id都是全域性唯一的,所以在main1中沒有1;在main2中沒有2。
靜態分析打包是事先生成chunk,inner1.js檔案被重複包含了,如果需要消除模組冗餘,可以通過CommonsChunkPlugin外掛來對公共依賴模組進行提取。
四、總結
- bundle.js檔案是一個立即執行函式表示式,傳入引數是一個陣列modules。真正執行module的是modules[moduleId].call(module.exports, module, module.exports, webpack_require);。
- modules陣列用來儲存模組初始化函式,裡面儲存著真正用到的模組內容以及一些id資訊。
- installedModules物件用來儲存module快取物件,方便其他模組使用。
- __webpack_require__模組載入函式,require時得到的物件,引數為模組id。
- installedModules[moduleId].exports === module.exports === webpack_exports
- 總的來說,webpack分析得到所有必須模組併合並;提供讓這些模組有序執行的環境。