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?");
    })
})
posted @ 2019-01-14 16:55  pluscat  阅读(1776)  评论(0编辑  收藏  举报