Webpack模塊化原理圖解

Webpack模塊化原理圖解

爲何須要模塊化

場景1

A同窗開發了模塊a,B同窗開發了模塊b,在頁面下經過以下方式進行引用node

<script src="a.js"></script>
<script src="b.js"></script>

這時模塊a,模板b中的代碼都暴露在全局環境中,若是模塊a中定義了一個方法del。同窗b並不知道,在模塊b中也定義了一個方法del。這時便形成了命名衝突的的問題。如圖webpack

場景2

C同窗開發了一個公共的工具庫utils.js,D同窗開發了一個公共的組件tab.jstab.js依賴utils.js。同窗E須要使用D同窗開發的tab.js,就須要經過以下方式引用web

<script src="util.js"></script>
<script src="tab.js"></script>

同窗E本身也開發了一個dailog.js同時它也依賴util.js。如今頁面同時引用了dialog.jstab.js,代碼以下緩存

<script src="util.js"></script>
<script src="dialog.js"></script>
<scrpt src="tab.js"></script>

同窗E不只須要同時引用這三個js文件,還必須保證文件之間的引用順序是正確的。同時,從上面的代碼咱們沒法直接看出模塊與模塊之間的依賴關係,若是不深刻tab.js,咱們沒法知道tab.js究竟是隻依賴util.js仍是dialog.js或者二者都依賴。隨着項目逐漸增大,不一樣模塊之間的依賴關係則會變的愈來愈難以維護也會致使許多模塊中大量的變量都暴露在全局環境中。模塊化

模塊化的幾種實現方案

模塊化的規範有不少種, 以下
| 規範 | 實現方案 |
| --- | --- |
| CommonJS | node.js |
| AMD | Require.js |
| CMD | Sea.js
| UMD | |
| ES6 Module | |函數

webpack支持CommonJS,AMD,ESModule等多種模塊化方式的語法工具

webpack的模塊化原理

在webpack中,一切皆模塊。下面咱們經過webpack來打包如下代碼。經過對打包代碼的解析,來一步一步的瞭解模塊化的實現原理。ui

目錄結構以下:spa

代碼以下:code

// webpack.config.js
const path = require('path');

module.exports = {
  entry: 'a.js',
  output: {
    path: path.resolve(__dirname, "dist"),
          filename: "[name].js"
    },
    resolve: {
          modules: [path.resolve(__dirname)]
    },
    optimization: {
          minimize: false
    }
}
// a.js
var b = require('b');

module.exports = b.text + ' world';
// b.js
exports.text = 'hello';

在simple目錄下執行webpack命令,會在simple目錄下生成dist/output.js文件。

// outout.js
// 代碼及註釋以下

(() => {
  // 全部導入的模塊都存儲在__webpack_modules__對象中,而且每一個模塊都要一個標識該模塊的id
  var __webpack_modules__ = ({
     847: ((module, __unsed_webpack_exports, __webpack_require__) => {
        // 模塊a...
     }),
     996: ((__unused_webpack_module, exports) => {
       // 模塊b...
     })
  })
  
  var __webpack_module_cache__ = {};

  function __webpack_require__(moduleId) {
      // 檢查緩存中不存在847導出對象,防止模塊847屢次執行
     if (__webpack_module_cache__[moduleId]) {
        // 從緩存中返回847導出對象
        return __webpack_module_cache__[moduleId].exports;
     }
     
     // 1.建立{exports: {}}對象
     // 2.像緩存中添加該對象,並讓該對象與模塊id 847相關聯
     var module = __webpack_module_cache__[moduleId] = {
       exports: {}
     }
     
     // 3.經過__webpack_modules__查詢模塊847
     // 4.執行模塊847並傳入剛剛建立的模塊847的導出對象module,以及module.exports等
     __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
     
     // 5.返回模塊847的導出對象
     return module.exports;
  }
  
  // 導入模塊847
  __webpack_require__(847);
})()

如上代碼咱們能夠知道,全部的的模塊都存儲在__webpack_modules__對象中,模塊的導出對象則存儲在__webpack_module_cache__對象中,咱們定義的模塊均可以經過傳入moduleId來調用__webpack_require__接口來訪問該模塊以及該模塊的導出對象。

當咱們經過__webpack_require__接口訪問模塊847的導出對象時。會先判斷__webpack_module_cache__對象中有沒有該模塊的導出對象,若是有的話,則直接返回。若是沒有的話則會

  • (1)建立模塊847的導出對象
var module = {
  exports: {}
}
  • (2)將該導出對象添加到__webpack_module_cache__對象中
__webpack_module_cahce__[moduleId] = module
  • (3)(4)經過__webpack_modules__查詢模塊847並執行
// 傳入模塊847的導出對象,以及require接口
__webpack_modules__[moduleId](module, module.exports, __webpack_require__)
  • (5)返回模塊847的導出對象
return module.exports

圖解以下:

  • 橘黃色背景的元素表明數據
  • 綠色背景的元素表明函數

總結

經過模塊化的管理方式

  1. 每一個模塊都經過函數封裝在本身的局部環境中。 模塊與模塊之間經過require 接口進行通訊。且不用經過暴露在全局環境。
  2. 每一個模塊都明確經過require接口傳入依賴的模塊,因此模塊與模塊之間的依賴關係也是很是明瞭的。如圖

a -> b -> c -> d

  • 模塊第1次被引用時,調用require接口會像cache中添加導出對象。並返回添加的導出對象。
  • 模塊被第2+次引用時,調用require接口查詢cache對象時,返回cache中對應的導出對象
相關文章
相關標籤/搜索