美文网首页
深入理解webpack打包原理

深入理解webpack打包原理

作者: z_love | 来源:发表于2019-08-06 16:54 被阅读0次

    commonjs模块打包代码

    module.exports = function() {
      console.log('math')
    }
    
    var math = require('./math')
    math()
    
    /******/ (function(modules) { // webpackBootstrap
    /******/    // The module cache
    /******/    var installedModules = {};
    /******/
    /******/    // The require function
    /******/    function __webpack_require__(moduleId) {
    /******/
    /******/        // Check if module is in cache
    /******/        if(installedModules[moduleId]) {
    /******/            return installedModules[moduleId].exports;
    /******/        }
    /******/        // Create a new module (and put it into the cache)
    /******/        var module = installedModules[moduleId] = {
    /******/            i: moduleId,
    /******/            l: false,
    /******/            exports: {}
    /******/        };
    /******/
    /******/        // Execute the module function
    /******/        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
    /******/
    /******/        // Flag the module as loaded
    /******/        module.l = true;
    /******/
    /******/        // Return the exports of the module
    /******/        return module.exports;
    /******/    }
    /******/
    /******/
    /******/    // expose the modules object (__webpack_modules__)
    /******/    __webpack_require__.m = modules;
    /******/
    /******/    // expose the module cache
    /******/    __webpack_require__.c = installedModules;
    /******/
    /******/    // define getter function for harmony exports
    /******/    __webpack_require__.d = function(exports, name, getter) {
    /******/        if(!__webpack_require__.o(exports, name)) {
    /******/            Object.defineProperty(exports, name, { enumerable: true, get: getter });
    /******/        }
    /******/    };
    /******/
    /******/    // define __esModule on exports
    /******/    __webpack_require__.r = function(exports) {
    /******/        if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
    /******/            Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
    /******/        }
    /******/        Object.defineProperty(exports, '__esModule', { value: true });
    /******/    };
    /******/
    /******/    // create a fake namespace object
    /******/    // mode & 1: value is a module id, require it
    /******/    // mode & 2: merge all properties of value into the ns
    /******/    // mode & 4: return value when already ns object
    /******/    // mode & 8|1: behave like require
    /******/    __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;
    /******/    };
    /******/
    /******/    // getDefaultExport function for compatibility with non-harmony modules
    /******/    __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 = "";
    /******/
    /******/
    /******/    // Load entry module and return exports
    /******/    return __webpack_require__(__webpack_require__.s = "./index.js");
    /******/ })
    /************************************************************************/
    /******/ ({
    
    /***/ "./index.js":
    /*!******************!*\
      !*** ./index.js ***!
      \******************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    eval("var math = __webpack_require__(/*! ./math */ \"./math.js\")\r\nmath()\n\n//# sourceURL=webpack:///./index.js?");
    /***/ }),
    
    /***/ "./math.js":
    /*!*****************!*\
      !*** ./math.js ***!
      \*****************/
    /*! no static exports found */
    /***/ (function(module, exports) {
    
    eval("module.exports = function() {\r\n  console.log('math')\r\n}\n\n//# sourceURL=webpack:///./math.js?");
    /***/ })
    
    /******/ });
    

    1.可以看出,使用commonjs打包的代码还是比较简单的,提炼主要代码如下

    (function(modules) {
      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;
        }
        return __webpack_require__(__webpack_require__.s = "./index.js");
      })({
        "./index.js": (function(module, exports, __webpack_require__) {
          eval("var math = __webpack_require__(\"./math.js\")\r\nmath()\n\n");
        }),
        "./math.js":
        (function(module, exports) {
          eval("module.exports = function() {\r\n console.log('math')\r\n}\n\n");
        })
      });
    

    可以看到,整个是个可执行函数,模块以文件名为key作为对象传入可执行函数中,第一句执行的是return webpack_require(webpack_require.s = "./index.js"),加载入口模块,调用函数webpack_require,会从缓存中取对应的模块,无法取到就会新建一个加入缓存中,然后调用入口key对应的包装function,执行eval中的代码,发现webpack_require("./math.js")就会去加载math.js模块并执行,然后赋值给缓存的module.exports并返回导出内容,赋值给var math,然后继续执行index模块中的下面的语句,执行完之后,同样会执行赋值和导出语句

    综上可得: 使用commonjs加载模块,是运行时加载,也就是递归加载和同步加载,当文件需要依赖其他文件时,先加载运行其他文件,返回结果后继续运行当前文件,并且只会加载一次,第二次加载会从缓存中取出第一次执行的结果.直接进行赋值操作,不会运行模块代码第二遍,由于是赋值操作,所以一般变量,是值的拷贝,不同模块引入模块的一般变量,都是通一个值,不同模块修改该值不会影响另一个模块,对于复杂数据类型,是浅拷贝,也就是地址拷贝,当在一个模块中修改地址指向的数据时,另一个模块中对应的数据会改变,原始模块中对应的值也会改变

    es6模块

    function math() {
      console.log('math')
    }
    
    export default math
    
    import math from './math'
    math()
    
    /******/ (function(modules) { // webpackBootstrap
    /******/    // The module cache
    /******/    var installedModules = {};
    /******/
    /******/    // The require function
    /******/    function __webpack_require__(moduleId) {
    /******/
    /******/        // Check if module is in cache
    /******/        if(installedModules[moduleId]) {
    /******/            return installedModules[moduleId].exports;
    /******/        }
    /******/        // Create a new module (and put it into the cache)
    /******/        var module = installedModules[moduleId] = {
    /******/            i: moduleId,
    /******/            l: false,
    /******/            exports: {}
    /******/        };
    /******/
    /******/        // Execute the module function
    /******/        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
    /******/
    /******/        // Flag the module as loaded
    /******/        module.l = true;
    /******/
    /******/        // Return the exports of the module
    /******/        return module.exports;
    /******/    }
    /******/
    /******/
    /******/    // expose the modules object (__webpack_modules__)
    /******/    __webpack_require__.m = modules;
    /******/
    /******/    // expose the module cache
    /******/    __webpack_require__.c = installedModules;
    /******/
    /******/    // define getter function for harmony exports
    /******/    __webpack_require__.d = function(exports, name, getter) {
    /******/        if(!__webpack_require__.o(exports, name)) {
    /******/            Object.defineProperty(exports, name, { enumerable: true, get: getter });
    /******/        }
    /******/    };
    /******/
    /******/    // define __esModule on exports
    /******/    __webpack_require__.r = function(exports) {
    /******/        if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
    /******/            Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
    /******/        }
    /******/        Object.defineProperty(exports, '__esModule', { value: true });
    /******/    };
    /******/
    /******/    // create a fake namespace object
    /******/    // mode & 1: value is a module id, require it
    /******/    // mode & 2: merge all properties of value into the ns
    /******/    // mode & 4: return value when already ns object
    /******/    // mode & 8|1: behave like require
    /******/    __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;
    /******/    };
    /******/
    /******/    // getDefaultExport function for compatibility with non-harmony modules
    /******/    __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 = "";
    /******/
    /******/
    /******/    // Load entry module and return exports
    /******/    return __webpack_require__(__webpack_require__.s = "./index.js");
    /******/ })
    /************************************************************************/
    /******/ ({
    
    /***/ "./index.js":
    /*!******************!*\
      !*** ./index.js ***!
      \******************/
    /*! no exports provided */
    /***/ (function(module, __webpack_exports__, __webpack_require__) {
    
    "use strict";
    eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./math */ \"./math.js\");\n\r\nObject(_math__WEBPACK_IMPORTED_MODULE_0__[\"default\"])()\n\n//# sourceURL=webpack:///./index.js?");
    
    /***/ }),
    
    /***/ "./math.js":
    /*!*****************!*\
      !*** ./math.js ***!
      \*****************/
    /*! exports provided: default */
    /***/ (function(module, __webpack_exports__, __webpack_require__) {
    
    "use strict";
    eval("__webpack_require__.r(__webpack_exports__);\nfunction math() {\r\n  console.log('math')\r\n}\r\n\r\n/* harmony default export */ __webpack_exports__[\"default\"] = (math);\n\n//# sourceURL=webpack:///./math.js?");
    
    /***/ })
    
    /******/ });
    
    /******/ (function(modules) { // webpackBootstrap
    /******/    // The module cache
    /******/    var installedModules = {};
    /******/
    /******/    // The require function
    /******/    function __webpack_require__(moduleId) {
    /******/
    /******/        // Check if module is in cache
    /******/        if(installedModules[moduleId]) {
    /******/            return installedModules[moduleId].exports;
    /******/        }
    /******/        // Create a new module (and put it into the cache)
    /******/        var module = installedModules[moduleId] = {
    /******/            i: moduleId,
    /******/            l: false,
    /******/            exports: {}
    /******/        };
    /******/
    /******/        // Execute the module function
    /******/        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
    /******/
    /******/        // Flag the module as loaded
    /******/        module.l = true;
    /******/
    /******/        // Return the exports of the module
    /******/        return module.exports;
    /******/    }
    /******/
    /******/
    /******/    // expose the modules object (__webpack_modules__)
    /******/    __webpack_require__.m = modules;
    /******/
    /******/    // expose the module cache
    /******/    __webpack_require__.c = installedModules;
    /******/
    /******/    // define getter function for harmony exports
    /******/    __webpack_require__.d = function(exports, name, getter) {
    /******/        if(!__webpack_require__.o(exports, name)) {
    /******/            Object.defineProperty(exports, name, { enumerable: true, get: getter });
    /******/        }
    /******/    };
    /******/
    /******/    // define __esModule on exports
    /******/    __webpack_require__.r = function(exports) {
    /******/        if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
    /******/            Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
    /******/        }
    /******/        Object.defineProperty(exports, '__esModule', { value: true });
    /******/    };
    /******/
    /******/    // create a fake namespace object
    /******/    // mode & 1: value is a module id, require it
    /******/    // mode & 2: merge all properties of value into the ns
    /******/    // mode & 4: return value when already ns object
    /******/    // mode & 8|1: behave like require
    /******/    __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;
    /******/    };
    /******/
    /******/    // getDefaultExport function for compatibility with non-harmony modules
    /******/    __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 = "";
    /******/
    /******/
    /******/    // Load entry module and return exports
    /******/    return __webpack_require__(__webpack_require__.s = "./index.js");
    /******/ })
    /************************************************************************/
    /******/ ({
    
    /***/ "./index.js":
    /*!******************!*\
      !*** ./index.js ***!
      \******************/
    /*! no exports provided */
    /***/ (function(module, __webpack_exports__, __webpack_require__) {
    
    "use strict";
    eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./math */ \"./math.js\");\n\n\n//# sourceURL=webpack:///./index.js?");
    
    /***/ }),
    
    /***/ "./math.js":
    /*!*****************!*\
      !*** ./math.js ***!
      \*****************/
    /*! exports provided: a */
    /***/ (function(module, __webpack_exports__, __webpack_require__) {
    
    "use strict";
    eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"a\", function() { return a; });\nvar a = 5\n\n//# sourceURL=webpack:///./math.js?");
    
    /***/ })
    
    /******/ });
    

    可以看到对于export default和export两种导出方式,webpack做的处理不一样,前者通过default导出,后者通过定义getter函数导出,所以模块导出的值,都是通过getter函数得到的,但是export default不是通过getter函数导出的,所以对于简单的变量来说,是值的复制,和commonjs表现一致,并且自己添加了严格模式

    webpack最核心的是__webpack_require__函数

    总结:

    • webpack只是打包模块的机制,不是任何规范,只是识别依赖进行打包,
      webpack会将es6Module,commonjs,requirejs等规范的模块进行打包
    • 由于都是经过webpack_require函数,所以三个模块可以混用,比如使用es6定义的模块,可以使用commonjs的方式进行调用
    • 每个模块第一次加载都会缓存,多次引入也只会执行一遍代码,第二次引入会从缓存中读取引出的变量,不会再执行一遍引入的模块,这点和script引入一次,然后挂载在window上是相通的

    相关文章

      网友评论

          本文标题:深入理解webpack打包原理

          本文链接:https://www.haomeiwen.com/subject/rquldctx.html