webpack 如何兼容 commonjs AMD es6模块化

2,581 阅读2分钟

     webpack 打包编译好的文件发现可以通过 import 或者 require 去引用,很好奇webpack 是如何做到的?webpack 是如何实现模块化的?

webpack 模块化原理

    webpack hack 了 commonjs,维护了自己的一套模块化系统,通过自定义的 exports 和 require 实现模块化,兼容了前端前面的模块化规范。

     新建文件 a.js 和 b.js,在 a.js 中通过 require 和 import 两种方式引入 b.js, 并使用 webpack4 打包生成 bundle.js。

// b.js

console.log('b.js');

export default 'b.js';

// a.js

import './b.js';

require('./b.js');

export default 'a.js';


// webpack.config.js

const webpack = require('webpack');

const path = require('path');

module.exports = {

 entry: './a.js', 

 mode: 'development', 

   output: { path: path.resolve(__dirname, 'dist'), 

   filename: 'bundle.js' 

 }

}

    生成的 bundle.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; 

     } 

     // Load entry module and return exports 

     return __webpack_require__(__webpack_require__.s = "./a.js"); })

 ({ 

   "./a.js": 

   (function(module, __webpack_exports__, __webpack_require__) {"use strict";eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _b_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./b.js */ \"./b.js\");\n\r\n__webpack_require__(/*! ./b.js */ \"./b.js\");\r\n\r\n/* harmony default export */ __webpack_exports__[\"default\"] = ('a.js');\r\n\n\n//# sourceURL=webpack:///./a.js?"); }), 

    "./b.js": 

    (function(module, __webpack_exports__, __webpack_require__) {"use strict";eval("__webpack_require__.r(__webpack_exports__);\nconsole.log('b.js');\r\n\r\n/* harmony default export */ __webpack_exports__[\"default\"] = ('b.js');\r\n\n\n//# sourceURL=webpack:///./b.js?"); 

    }) 

});

     bundle.js 本质上是自执行函数,自执行函数的入参是包含所有模块的对象,以模块名称作为 key 值,value 为包裹在函数内的模块,自执行函数体内是处理模块化的逻辑,installedModules 缓存模块,关键在于 __webpack_require__ 函数,函数内部维护着 modules 对象

var module = installedModules[moduleId] = {

   i: moduleId,

   l: false,

   exports: {}

}; 

     __webpack_require__ 函数内执行了自执行函数传入的模块,并将 module、module.exports、__webpack_require_ 传入

modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

return module.exports;

    再来看下 a.js 模块函数的执行, 发现 import 和 require 都被 __webpack_require__ 替换了

  (function(module, __webpack_exports__, __webpack_require__) {"use strict";eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _b_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./b.js */ \"./b.js\");\n\r\n__webpack_require__(/*! ./b.js */ \"./b.js\");\r\n\r\n/* harmony default export */ __webpack_exports__[\"default\"] = ('a.js');\r\n\n\n//# sourceURL=webpack:///./a.js?"); })

    所以 webapck 其实就是在内部实现了 require 和 exports,然后自动加载入口模块,根据依赖加载其他模块,并且控制模块缓存。