webpack cjs運行時分析示例詳解

準備工作(接上篇文章的示例也可以):

1. 在index.js文件中引入任一js文件

import sum from './sum';

const result = sum(1,2);
console.log(result);

2. sum文件

const sum = (a, b) => {
  return a+b;
}

export default sum

3. build.js文件

const path = require('path');
const webpack = require('webpack');

function wrapBuild() {
  return webpack({
    entry: './index.js',
    mode: 'none',
    output: {
      path: path.resolve(__dirname, 'build'),
      filename: '[name].js'
    },
    infrastructureLogging: {
      debug: true,
      level: 'log',
    }
  })
}

wrapBuild().run((err, stat) => {
  const startTime = stat.startTime;
  const endTime = stat.endTime;
  console.log('構建時間: ', endTime - startTime);
})

4. 命令行執行node ./build.js 生成打包產物main.js。

(截圖未完整, 共87行)

5. 什麼是運行時代碼?

通過webpack打包得到的文件(如/build/main.js的骨架代碼), 其中包含瞭一些webpack如何將多個模塊集合在一起的代碼, 可粗暴理解為webpack runtime 打包過後的代碼。可通過node build/main.js可直接調試。

6. 示例打包js資源後的運行時代碼分析:(含註解)

/******/ (() => { // webpackBootstrap
/******/ 	"use strict";
/******/ 	var __webpack_modules__ = ([
/* 0 */,    // ---模塊0, 入口模塊 可以理解為index.js模塊
/* 1 */    // ---模塊1,打包過後的sum.js
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {

__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "default": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
const sum = (a, b) => {
  return a+b;
}

/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (sum);

/***/ })
/******/ 	]);
/************************************************************************/
/******/ 	// 模塊緩存
/******/ 	var __webpack_module_cache__ = {};
/******/ 	
/******/ 	// 模塊加載器, 模擬實現common.js的require
/******/ 	function __webpack_require__(moduleId) {
/******/ 		// 根據moduleId從緩存中讀取module
/******/ 		var cachedModule = __webpack_module_cache__[moduleId];
            // 若緩存中存在該模塊,則返回當前的module.exports
/******/ 		if (cachedModule !== undefined) {
/******/ 			return cachedModule.exports;
/******/ 		}
/******/ 		// 緩存中不存在該模塊,則去__webpack_modules__裡取。
                // 創建一個新的模塊,  寫入cache對象, key為moduleId, value 為 exports對象
/******/ 		var module = __webpack_module_cache__[moduleId] = {
/******/ 			// no module.id needed
/******/ 			// no module.loaded needed
/******/ 			exports: {}
/******/ 		};
/******/ 	
/******/ 		// 調用包裹函數,計算模塊信息,解析出相關模塊內容(如:exports)
/******/ 		__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/ 	
/******/ 		// 返回當前解析過後的module.exports
/******/ 		return module.exports;
/******/ 	}

var __webpack_exports__ = {};
// 加載入口函數
(() => {
__webpack_require__.r(__webpack_exports__);
/* harmony import */ var _sum__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
const result = (0,_sum__WEBPACK_IMPORTED_MODULE_0__["default"])(1,2);
console.log(result);
})();

/******/ })()
;

該運行時代碼一共做瞭三件事:

  • __webpack_modules__ : 維護一個所有模塊的數組,通過深度優先遍歷將其全部轉換為AST,以供給後面的包裹函數解析
  • __webpack_require__: 手動實現一個模塊加載器;優先從緩存列表__webpack_module_cache__中讀取模塊,並返回其exports內容。如果緩存中沒有,則調用包裹函數解析出模塊exports等重要內容,存入緩存列表中並返回exports。
  • 調用__webpack_require_(0): 運行入口模塊.

7. 疑問?

  • 如果不對模塊進行緩存,會有什麼問題?
  • 模塊內容重復計算,消耗性能。
  • 每個模塊隻在第一次引用的時候產生一個對象,後面都是引用該對象,減少代碼復雜度。
  • module.exports和exports有什麼區別?
  • 默認情況下,Node準備的exports變量和module.exports變量實際上是同一個變量,並且初始化為空對象,我們可以把要輸出的東西直接加入在這個空對象裡面;但是,如果我們要輸出的是一個函數或數組,那麼,隻能給module.exports賦值,給exports賦值是無效的,因為賦值後,module.exports仍然是空對象。
  • 結論:
  • 如果要輸出一個鍵值對象{},可以利用exports這個已存在的空對象{},並繼續在上面添加新的鍵值;
  • 如果要輸出一個函數或數組,必須直接對module.exports對象賦值。
  • 建議直接對module.exports賦值,可以應對任何情況;

參考文獻

詳細的可移步大佬的文章

到此這篇關於webpack cjs運行時分析的文章就介紹到這瞭,更多相關webpack cjs運行時內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: