webpack如何支持的es于cjs模块化

2024-8-4 1,085 8/4

项目中遇到了循环依赖的问题,便浅浅分析一下。。。

基于打包后的代码逆向分析 webpack是如何实现es与cjs这两种模块化的

测试环境搭建:webpack如何支持的es于cjs模块化

 

依赖

{
  "name": "test",
  "version": "0.0.1",
  "description": "",
  "author":{},
  "main": "index.js",
  "license": "MIT",
  "engines": {
    "node": ">=8.11.1"
  },
  "scripts": {
    "build": "webpack --config webpack.config.js"
  },
  "dependencies": {

  },
  "devDependencies": {
    "webpack": "^5.73.0",
    "webpack-cli": "^4.10.0"
  }
}

webpackConfig

const path = require('path');
module.exports = {
    mode: "development",
    devtool: false,
    entry: {
        main: "./src/test.js",
    },
    output: {
        filename: "main.js", 
        path: path.resolve(__dirname, "./dist"), 
    },
};

测试文件:

test.js

const com = require('./com')
console.log('%c +>>>>>>>>>>','color:red')
console.log('引入的模块')
console.dir(com)

com.js

console.log('导出模块测试')
module.exports = {
    name: 'lz'
}

打包后的源码

/******/ (() => {
  // webpackBootstrap
  /******/ var __webpack_modules__ = {
    /***/ "./src/com.js":
      /*!********************!*\
  !*** ./src/com.js ***!
  \********************/
      /***/ (module) => {
        console.log("导出模块测试");
        module.exports = {
          name: "lz",
        };

        /***/
      },

    /******/
  };
  /************************************************************************/
  /******/ // The module cache
  /******/ var __webpack_module_cache__ = {};
  /******/
  /******/ // The require function
  /******/ function __webpack_require__(moduleId) {
    /******/ // Check if module is in cache
    /******/ var cachedModule = __webpack_module_cache__[moduleId];
    /******/ if (cachedModule !== undefined) {
      /******/ return cachedModule.exports;
      /******/
    }
    /******/ // Create a new module (and put it into the cache)
    /******/ var module = (__webpack_module_cache__[moduleId] = {
      /******/ // no module.id needed
      /******/ // no module.loaded needed
      /******/ exports: {},
      /******/
    });
    /******/
    /******/ // Execute the module function
    /******/ __webpack_modules__[moduleId](
      module,
      module.exports,
      __webpack_require__
    );
    /******/
    /******/ // Return the exports of the module
    /******/ return module.exports;
    /******/
  }
  /******/
  /************************************************************************/
  /*!*********************!*\
  !*** ./src/test.js ***!
  \*********************/
  const com = __webpack_require__(/*! ./com */ "./src/com.js");
  console.log("%c +>>>>>>>>>>", "color:red");
  console.log("引入的模块");
  console.dir(com);
  /******/
})();

其实主要是关注require方法的实现,

首先就是定义modules,key为路径,value是模块的源代码

定义cacheModules, 用于缓存模块的内容

moduleId为路径
实现require方法:先是在cacheModules里面找有没有对应的缓存,有的话之间return   cachedModule.exports; 没有则给cachedModule[moduleId]赋值 {exports:{ }}

然后执行modules[modukeId],将cachedModule[moduleId]作为参数参数传递,因为modules[modukeId]会更改传入的module的exports属性的值为exports的值,最后return cachedModule[moduleId].exports
en,代码有出入,不过思想是一致的。

那么搞懂原理后,可以想一下如何处理循环依赖的

webpack如何支持的es于cjs模块化
引入com时,因为modules里面没有,所以会先给他赋值  exports = {},然后之前其源码,但是在com中有引入comTest,在require('./coomTest')会执行comTest的源码,但是comTets中也有引入com,但是,这时在cacheModules中已经定义了com的exports,所以会直接return,然后继续执行下一步即可,简单来说,循环依赖是通过modulesCache去解决的,

if (cachedModule !== undefined) {
      return cachedModule.exports;
}

 

 

在看一下es规范实现的模块化的处理

其实是差不多的,模块加载的实现仍然是require方法, 区别的点在与es是直接通过export 和 export default进行暴露的,这时就没有module进行绑定了

看一下打包之后的代码

/******/ (() => {
  // webpackBootstrap
  /******/ "use strict";
  /******/ var __webpack_modules__ = {
    /***/ "./src/com.js":
      /*!********************!*\
  !*** ./src/com.js ***!
  \********************/
      /***/ (
        __unused_webpack_module,
        __webpack_exports__rwfXZjj,
        __webpack_require__
      ) => {
        __webpack_require__.r(__webpack_exports__);
        /* harmony export */ __webpack_require__.d(__webpack_exports__, {
          /* harmony export */ default: () => __WEBPACK_DEFAULT_EXPORT__,
          /* harmony export */ obj: () => /* binding */ obj,
          /* harmony export */
        });
        console.log("导出模块测试");
        /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = {
          name: "lz",
        };

        const obj = {
          name: "zp",
        };

        /***/
      },

    /******/
  };
  /************************************************************************/
  /******/ // The module cache
  /******/ var __webpack_module_cache__ = {};
  /******/
  /******/ // The require function
  /******/ function __webpack_require__(moduleId) {
    /******/ // Check if module is in cache
    /******/ var cachedModule = __webpack_module_cache__[moduleId];
    /******/ if (cachedModule !== undefined) {
      /******/ return cachedModule.exports;
      /******/
    }
    /******/ // Create a new module (and put it into the cache)
    /******/ var module = (__webpack_module_cache__[moduleId] = {
      /******/ // no module.id needed
      /******/ // no module.loaded needed
      /******/ exports: {},
      /******/
    });
    /******/
    /******/ // Execute the module function
    /******/ __webpack_modules__[moduleId](
      module,
      module.exports,
      __webpack_require__
    );
    /******/
    /******/ // Return the exports of the module
    /******/ return module.exports;
    /******/
  }
  /******/
  /************************************************************************/
  /******/ /* webpack/runtime/define property getters */
  /******/ (() => {
    /******/ // define getter functions for harmony exports
    /******/ __webpack_require__.d = (exports, definition) => {
      /******/ for (var key in definition) {
        /******/ if (
          __webpack_require__.o(definition, key) &&
          !__webpack_require__.o(exports, key)
        ) {
          /******/ Object.defineProperty(exports, key, {
            enumerable: true,
            get: definition[key],
          });
          /******/
        }
        /******/
      }
      /******/
    };
    /******/
  })();
  /******/
  /******/ /* webpack/runtime/hasOwnProperty shorthand */
  /******/ (() => {
    /******/ __webpack_require__.o = (obj, prop) =>
      Object.prototype.hasOwnProperty.call(obj, prop);
    /******/
  })();
  /******/
  /******/ /* webpack/runtime/make namespace object */
  /******/ (() => {
    /******/ // define __esModule on exports
    /******/ __webpack_require__.r = (exports) => {
      /******/ if (typeof Symbol !== "undefined" && Symbol.toStringTag) {
        /******/ Object.defineProperty(exports, Symbol.toStringTag, {
          value: "Module",
        });
        /******/
      }
      /******/ Object.defineProperty(exports, "__esModule", { value: true });
      /******/
    };
    /******/
  })();
  /******/
  /************************************************************************/
  var __webpack_exports__ = {};
  /*!*********************!*\
    !*** ./src/test.js ***!
    \*********************/
  __webpack_require__.r(__webpack_exports__);
  /* harmony import */ var _com__WEBPACK_IMPORTED_MODULE_0__ =
    __webpack_require__(/*! ./com */ "./src/com.js");

  console.log("%c +>>>>>>>>>>", "color:red");
  console.log("引入的模块");
  console.dir(_com__WEBPACK_IMPORTED_MODULE_0__.obj);
  console.dir(_com__WEBPACK_IMPORTED_MODULE_0__["default"]);
  /******/
})();

区别最大的点在于 打包之后模块的源码,多了两个步骤的处理

第一步是给module打上es的tag,区分这是es模块,是通过Object.defineProperty 去exports加的一层处理,修改了元数据

第二步是给exports赋值,default exports其实也就是绑定default属性,

__webpack_require__.d(__webpack_exports__, {
default: () => __WEBPACK_DEFAULT_EXPORT__,
 obj: () =>  obj,
});

那么余下的步骤就与cjs是一致的。

 

那么还有个问题,通过es的规范家在cjs的模块,是如何处理的

因为require方法 return 的就是 { exports: {} } 的形式,而es的本质也是通过require进行导入的,那么他们拿到的数据的结构就是一致的,只需要在做一层转化即可,那么区分es与cjs自然是通过之前打的tag区分的

/******/ 	(() => {
/******/ 		// getDefaultExport function for compatibility with non-harmony modules
/******/ 		__webpack_require__.n = (module) => {
/******/ 			var getter = module && module.__esModule ?
/******/ 				() => (module['default']) :
/******/ 				() => (module);
/******/ 			__webpack_require__.d(getter, { a: getter });
/******/ 			return getter;
/******/ 		};
/******/ 	})();
/******/ 	
/******/ 	/* webpack/runtime/define property getters */
/******/ 	(() => {
/******/ 		// define getter functions for harmony exports
/******/ 		__webpack_require__.d = (exports, definition) => {
/******/ 			for(var key in definition) {
/******/ 				if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ 					Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ 				}
/******/ 			}
/******/ 		};
/******/ 	})();

 

TODO 后续更新require.js与sea.js

- THE END -
2

非特殊说明,本博所有文章均为博主原创。

共有 0 条评论