作者提肛
-
webpack里 esm 如何和 cjs 兼容
-
在ts中 cjs 如何和 esm 兼容
-
ts中cjs为什么能用import
什么是cjs和ESM
ESM 全称是 ESModule 是 web 系统的模块化,ESModule 输出的是 值的引用
cjs 全称 CommonJS 是 nodejs 的模块化,CommonJS输出的是 值的拷贝
这里不做过多的介绍,更多介绍可以自己从掘金上搜索相关差异
webpac里esm是如何做向下兼容cjs
(1) webpack是如何打包cjs方式
先看下webpack的打包出来的关键内容
var __webpack_modules__ = {
'./src/index.js': ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _esm__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./esm */ \"./src/esm.js\");\nconst v = __webpack_require__(/*! ./cjs */ \"./src/cjs.js\");\n\nv()\n;(0,_esm__WEBPACK_IMPORTED_MODULE_0__[\"default\"])()\n\n//# sourceURL=webpack://webpack/./src/index.js?");
}),
'./src/esm.js': ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
eval("__webpack_require__.r(__webpack_exports__); __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\nfunction m (){\n console.log('>>><><><')\n}\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (m);\n\n//# sourceURL=webpack://webpack/./src/esm.js?");
}),
'./src/cjs.js': ((module) => {
eval("function v() {\n console.log('>>>>>>>')\n}\nmodule.exports = v\n\n//# sourceURL=webpack://webpack/./src/cjs.js?");
})
}
function __webpack_require__(moduleId){
var module = {
exports: {}
}
__webpack_modules__[moduleId](module, module.exports, __webpack_require__)
return module.exports
}
(()=>{
__webpack_require__.d = () => {
__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_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
__webpack_require__.r = (exports) => {
Object.defineProperty(exports, '__esModule', { value: true });
}
})()
var __webpack_exports__ = __webpack_require__("./src/index.js");
PS: 这里对webpack打包机制有问题的同学,可以找下webpack相关知识
其实 webpack 都是通过 module.exports 做的兼容处理。
当处理cjs时候。
模块化的代码最后会打包成一个对象结构,然后通过路径key(./src/index)找到对应的string代码块,然后会执行 __webpack_require__ 函数
这里 cjs 模块化分为两种方式一种是 module.exports 和 exports
接下来看下 __webpack_require__ 是如何执行 module.exports 方式
var __webpack_modules__ = {
'./src/cjs.js': (module) => {
let v = 1
module.exports = v
}
}
function __webpack_require__(src){
let module = {
exports:{}
}
__webpack_modules__[src](module)return module.exports}
__webpack_require__('./src/cjs.js')
执行 __webpack_require__ 的时候会把module对象传进去,类似module.exports = v,最后return module.exports
接下来看下 __webpack_require__ 是如何执行 exports 方式
var __webpack_modules__ = {
'./src/cjs.js': (module, exports) => {
let v = 1
exports = v
}
}
function __webpack_require__(src){
let module = {
exports:{}
}
__webpack_modules__[src](module, module.exports)
return module.exports
}
__webpack_require__('./src/cjs.js')
当 exports 方式时,执行 __webpack_require__ 函数时会多传入一个参数,第二个参数会直接传入 module.exports
其实就是 v = exports = module.exports
(2) webpack如何打包esm
先展示下代码
var __webpack_modules__ = {
'./src/cjs.js': (module, exports, __webpack_require__) => {
__webpack_require__.r(exports)
__webpack_require__(__webpack_require__.n(exports: {
default: 1
}))
}
}
function __webpack_require__(src){
let module = {
exports:{}
}
__webpack_modules__[src](module, module.exports, __webpack_require__)
return module.exports
}
__webpack_require__.r = (exports) => {
Object.defineProperty(exports, '__esModule', { value: true })
}
__webpack_require__.n = (exports) => {
exports.__esModule ? exports['default'] : exports}__webpack_require__('./src/cjs.js')
我这里做了非常大的简化,
其实说白了就是当检测是__esModule 时,就会从对象中再多找一层 default
总结:在webpack中如果是cjs的就会返回module.exports 当遇到检测到是esm时,就会返回
module.exports.default 。其实就是多了一次default
在ts中cjs如何向上兼容esm
咋们还是先上代码
var __importDefault = function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const cjs = require("cjs.js");const esm = __importDefault(require("esm.js"));
总结:ts会把 import方式最后打包成require方式 ,ts遇到当前是esm模式的就会把require方法放到**__importDefault** 函数里面,其实本质也是多找一层defualt
那ts是如何做到cjs支持import方式导入的呢
ts中cjs为什么能用import
简单看下ast解析cjs和esm的结果
import importCjs from './cjs.js';
const requireCjs = require('./cjs.js')
其实在ast里面,我是指将import cjs from './cjs'转换成const cjs = require('cjs')
我们看下具体的ast代码实现
const plugin = declare((api) => {
return {
visitor: {
ImportDeclaration(path, state) {
const specifiers = path.get('specifiers.0')
const source = path.get('source').toString();
const name = specifiers.get('local').toString()
const v = api.template.ast(`const ${name} = require(${source})`)
path.replaceWith(v)
}
}
}})
module.exports = plugin
总结:找到****cjs,然后找到path路径,通过api.template直接转换为require就好了