1. 程式人生 > >webpack-bundle.js原理

webpack-bundle.js原理

bundle.js

原始碼

//a.js
import { log } from './b.js'
log('hello')
//b.js
export const log = function (m) {
  console.log(m)
}

export const error = function (m) {
  console.error(m)
}

自執行函式

//其中 module0 和 module1 是我們的 a 和 b兩個模組,不過也被一個函式包起來了。這段程式碼會把我們的模組放入一個數組,傳給自執行函式,他來負責呼叫模組。

(function(modules) {
    var installedModules = {};
    function __webpack_require__(moduleId) {}
    // Load entry module and return exports
    return __webpack_require__(__webpack_require__.s = 0);
})
(function (modules) {})([module0, module1])

require模組

function __webpack_require__(moduleId){
        //檢查模組是否在快取中
        if(installedModules[moduleId]) {
            return installedModules[moduleId].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;
}

require靜態方法

__webpack_require__.m = modules;//獲取模組列表
__webpack_require__.c = installedModules; //獲取快取模組列表
__webpack_require__.d = function(exports, name, getter) {//定義一個getter方法
        if(!__webpack_require__.o(exports, name)) {
            Object.defineProperty(exports, name, { enumerable: true, get: getter });
        }
}
  //如果此exports物件__esModule屬性為true的話,表示這是一個es6的模組
__webpack_require__.r = function(exports) {
        if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
            Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
        }
        Object.defineProperty(exports, '__esModule', { value: true });
}

 /** 
     * 建立一個偽名稱空間物件
     * mode & 1 value 是一個模組id,require(value)載入模組
     * mode & 8 將value的所有屬性合併到ns物件當中
     * mode & 4 返回已經是nsobject到value
     * mode & 8 | 1 繫結getter方法
    */
 __webpack_require__.t = function(value, mode) {
        if(mode & 1) value = __webpack_require__(value);
        if(mode & 8) return value;
        if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
        var ns = Object.create(null);
        __webpack_require__.r(ns);
        Object.defineProperty(ns, 'default', { enumerable: true, value: value });
        if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
        return ns;
 }
//相容模組物件,給模組物件繫結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;
}

//Object.prototype.hasOwnProperty.call判斷物件有沒有某個屬性
__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };

// __webpack_public_path__
__webpack_require__.p = "";

依賴關係


因為webpack從entry開始,對每一個 module 都進行處理,碰到 require 之後就跳入到對應的 module 的處理,也就是遞迴的對這顆依賴樹進行處理,這是典型的深度優先遍歷的遞迴解法,而且是先序優先遍歷。處理的過程是這樣的

處理 main.js,記錄入口 [main]
碰到 require(a),記錄 [main, a]
進入到 a 模組,碰到語句 require(c), 記錄下來 [main, a, c]
同理碰到 require(d),記錄下來 [main, a, c, d]
返回到 main.js,下一句是 require('b'),記錄下來 [main, a, c, d, b]
進入模組 b,碰到語句 require(e),記錄下來[main, a, c, d, b, e]
返回,結束

打包後代碼

(function(modules){
     /** 
     * {
     *   "./src/b.js":fn(...),
     *   "./src/test.js":fn(...)
     * }
     */
    //快取模組
    var installedModules = {};

    //引入模組的方法
    function __webpack_require__(moduleId){
        //檢查模組是否在快取中
        if(installedModules[moduleId]) {
            return installedModules[moduleId].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;
    }
    
    //掛在靜態方法
    __webpack_require__.m = modules;//獲取模組列表
    __webpack_require__.c = installedModules; //獲取快取模組列表
     /**
        let obj={};
        let age='age';
        function getter() {
            return 9;
        }
        Object.defineProperty(obj,age,{enumerable: true,get: getter});
        console.log(obj.age)
    */
    //檢查exports物件上有沒有掛載name屬性,沒有就掛載一個
    __webpack_require__.d = function(exports, name, getter) {//定義一個getter方法
        if(!__webpack_require__.o(exports, name)) {
            Object.defineProperty(exports, name, { enumerable: true, get: getter });
        }
    }
     /**
      * 物件的Symbol.toStringTag屬性,指向一個方法
      * 在該物件上面呼叫Object.prototype.toString方法時,如果這個屬性存在,它的返回值會出現在toString方法返回的字串之中,表示物件的型別
      * 也就是說,這個屬性可以用來定製[object Object]或[object Array]中object後面的那個字串
      * ({[Symbol.toStringTag]: 'Foo'}.toString())  "[object Foo]"
      */
      // define __esModule on exports 在匯出物件上定義__esModule屬性
      //如果此exports物件__esModule屬性為true的話,表示這是一個es6的模組
    __webpack_require__.r = function(exports) {
        if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
            Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
        }
        Object.defineProperty(exports, '__esModule', { value: true });
    }

    /** 
     * 建立一個偽名稱空間物件
     * mode & 1 value 是一個模組id,require(value)載入模組
     * mode & 8 將value的所有屬性合併到ns物件當中
     * mode & 4 返回已經是nsobject到value
     * mode & 8 | 1 繫結getter方法
    */
    __webpack_require__.t = function(value, mode) {
        if(mode & 1) value = __webpack_require__(value);
        if(mode & 8) return value;
        if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
        var ns = Object.create(null);
        __webpack_require__.r(ns);
        Object.defineProperty(ns, 'default', { enumerable: true, value: value });
        if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
        return ns;
    }

    //相容模組物件,給模組物件繫結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;
    }
    //Object.prototype.hasOwnProperty.call判斷物件有沒有某個屬性
    __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };

    // __webpack_public_path__
    __webpack_require__.p = "";

    //__webpack_require__.s 模組id
    return __webpack_require__(__webpack_require__.s = "./src/test.js");
})
({
    "./src/b.js":(function(module, __webpack_exports__, __webpack_require__){
        "use strict";
        //判斷下exports是否是es模組內部方法掛載到exports物件上
        eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"log\", function() { return log; });\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"error\", function() { return error; });\nconst log = function (m) {\n    console.log(m)\n  }\n  \n  const error = function (m) {\n    console.error(m)\n  }\n\n//# sourceURL=webpack:///./src/b.js?");
    }),
    "./src/test.js":(function(module, __webpack_exports__, __webpack_require__){
        "use strict";
        //主模組,載入bmodule,並執行引入的module
        eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _b_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./b.js */ \"./src/b.js\");\n\nObject(_b_js__WEBPACK_IMPORTED_MODULE_0__[\"log\"])('hello')\n\n//# sourceURL=webpack:///./src/test.js?");
    })
})