淺談Webpack是如何打包CommonJS的

CommonJS 是 Node 中的一種模塊化規范,其是一種運行在 Node 環境下的代碼,這種代碼是不能直接運行到瀏覽器環境中的。但是在日常使用 webpack 的項目中不用做額外的處理,我們也能使用 CommonJS 來書寫代碼,那麼 webpack 在這背後做瞭什麼呢?

我們這裡不看編譯時,隻看運行時

一、書寫代碼

使用yarn init -y命令初始化一個package.json文件。 接著yarn add webpack安裝一下webpack。 目錄下創建一個index.js內容如下:

const sum = require('./sum')

console.log(sum(1, 2))

sum.js文件內容如下:

module.exports = (...args) => args.reduce((x, y) => x + y, 0)

二、使用webpack打包編譯

這裡不想寫webpack的配置文件然後再通過 webpack-cli 來打包,就直接寫一個打包的文件。

// build.js
const webpack = require('webpack')

function f1() {
	return webpack({
		entry: './index.js',
		mode: 'none',
		output: {
			iife: false,
			pathinfo: 'verbose' // verbose: 冗餘;盡可能的詳細
		}
	})
}

f1().run((err, stat) => {
	console.log('打包')
})

接著在終端跑一下這個文件

node build.js

如果成功的話就會出來一個dist目錄,裡面有個main.js總共就是50行代碼,其中大部分都是註釋,代碼如下

var __webpack_modules__ = ([
/* 0 */,
  /* 1 */
  /*!****************!*\
    !*** ./sum.js ***!
    \****************/
  /*! unknown exports (runtime-defined) */
  /*! runtime requirements: module */
  /*! CommonJS bailout: module.exports is used directly at 1:0-14 */
  ((module) => {
    module.exports = (...args) => args.reduce((x, y) => x + y, 0)
  })
]);
/************************************************************************/
// The module cache
var __webpack_module_cache__ = {};

// The require function
function __webpack_require__(moduleId) {
  // Check if module is in cache
  var cachedModule = __webpack_module_cache__[moduleId];
  if (cachedModule !== undefined) {
    return cachedModule.exports;
  }
  // Create a new module (and put it into the cache)
  var module = __webpack_module_cache__[moduleId] = {
    // no module.id needed
    // no module.loaded needed
    exports: {}
  };

  // Execute the module function
  __webpack_modules__[moduleId](module, module.exports, __webpack_require__);

  // Return the exports of the module
  return module.exports;
}

/************************************************************************/
var __webpack_exports__ = {};
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
(() => {
  /*!******************!*\
    !*** ./index.js ***!
    \******************/
  /*! unknown exports (runtime-defined) */
  /*! runtime requirements: __webpack_require__ */
  const sum = __webpack_require__(/*! ./sum */ 1)

  console.log(sum(1, 2))
})();

三、解析

我們再把代碼精簡一下,並加上註釋

// 存放的模塊,是一個數組
const __webpack_modules__ = [
  ,
  ((module) => {
    // sum.js 中的內容
    module.exports = (...args) => args.reduce((x, y) => x + y, 0)
  })
]

// 模塊緩存(也就是說如果模塊已經被引用過瞭就直接從這兒拿)
const __webpack_module_cache__ = {}

// moduleId 為 __webpack_modules__ 的下標
function __webpack_require__(moduleId) {
  // 如果能從緩存裡面拿到,則直接返回
  const cachedModule = __webpack_module_cache__[moduleId]
  if (cachedModule !== undefined) return cachedModule.exports
  
  // 緩存內拿不到,則創建一個對象同時內部包含一個 exports 對象並存入到緩存內
  const module = __webpack_module_cache__[moduleId] = {
    exports: {}
  }
  
  // 接著通過執行 __webpack_modules__  中的moduleId對應函數並傳入 module 對象
  // 通過函數內賦值 module.exports 獲得 sum 函數
  __webpack_modules__[moduleId](module, module.exports, __webpack_require__)
  
  // 最後返回 module 中的 exports 對象
  return module.exports
}

(() => {
  // index.js 中的內容
  const sum = __webpack_require__(1)

  console.log(sum(1, 2))
})();

我們通過註釋配合來解析一下

  • 首先執行立即執行函數(L32)中的__webpack_require__函數,並傳入moduleId 為 1
  • __webpack_require__函數中嘗試在__webpack_module_cache__中獲取moduleId為 1 的模塊(L16)
  • __webpack_module_cache__中獲取失敗之後創建一個object,同時內部有屬性exports = {},並同時將其賦值給__webpack_module_cache__[moduleId](L20)
  • 執行對應的moduleId的函數__webpack_modules__[moduleId],同時將module對象作為入參,在函數內將sum函數賦值給參數module.exports對象(L6),如此module.exports就是sum函數瞭
  • 然後在__webpack_require__中返回 module.exports
  • 執行完__webpack_require__(1)以後將其返回值賦值給sum(L34)
  • 最後就可以調用sum函數瞭(L36)

到此這篇關於淺談Webpack是如何打包CommonJS的的文章就介紹到這瞭,更多相關Webpack打包CommonJS內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: