Node CJS 和ESM 混合开发
禁止 module.exports 和 exports.fn 混用
解决filename,dirname无法直接使用
导入pkg的方法
或者放入到立即执行函数中 async await可以使用
不建议混用,建议使用esModule,因为ESM兼容commonjs
常见错误
引入纯 ESM 模块化的第三方包
https://blog.csdn.net/xs20691718/article/details/122727795?spm=1001.2014.3001.5502
引入一个概念 Pure ESM package,也就是纯 ESM 模块化的包, 如果要使用这种第三方库,可以阅读文档:https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
如果一个库是 Pure ESM package 的话,它就没办法再被 commonjs 标准的代码使用 require 引用了,如果要解决这个问题,文档中提出了三种方案:
- Use ESM yourself. **(preferred)**Use import foo from 'foo' instead of const foo = require('foo') to import the package. You also need to put "type": "module" in your package.json and more. Follow the below guide.
- If the package is used in an async context, you could use await import(…) from CommonJS instead of require(…).
- Stay on the existing version of the package until you can move to ESM.
关于 nodejs 中如何处理 ES6 模块的,可以参考:https://www.ruanyifeng.com/blog/2020/08/how-nodejs-use-es6-module.html
Use ESM yourself 第一种方式比较扯,就是把你自己的库也改成 ESM 标准,这就很坑了,这不是扩大了兼容性的问题了嘛。
use await import(…) 第二种方式,就是将静态的 import 语句,改为动态的 import() 方法,例如:
// before
import { xxx } from 'globby';
// after
const { xxx } = await import('globby');
理论上讲好像可以,但是我实际尝试的时候,发现如果 tsc 编译后为 commonjs 标准的话, import()
方法会被转化为一个__importStar(require('globby'))
方法,本质上还是 require()
?所以还是会报错。
需要进一步调研看看。
- Stay on the existing version of the package until you can move to ESM 这个方法也很扯淡,就是在你可以将你的项目改为 ESM 标准之前,使用旧版本的 commonjs 标准的第三方库。
解决方法 上面的三种方式,都没有解决问题,只能采取一种治标不治本的方式了。
既然第三方库是 ESM 标准,那么我们在 tsc 编译时,把它也编译一下好了。
以 globby 为例,在 tsconfig 文件中加入以下代码:
{
"compilerOptions": {
...
// 因为 globby 是用 js 写的,所以在 tsconfig 中要将 allowJs 设置为 true
"allowJs": true
},
"include": [
"node_modules/globby/**/*"
]
}
此时再运行 tsc 编译,会发现在输出的 dist 目录中,新增了一个 node_modules 目录,其中包含了编译后的 globby 包代码。
但是这里需要注意下,再次运行项目,发现还是报同样的错,只是报错的库由 globby 变成了 array-union,这 是因为 globby 是 pure ESM package,经过 tsc 编译后变成了 commonjs 标准,但是 globby 引用了 array-union,而 array-union 也是 pure ESM package。
以此类推,需要把所有的 pure ESM package 都编译一下:
{
"include": [
"node_modules/globby/**/*",
"node_modules/array-union/**/*",
"node_modules/slash/**/*"
]
}
完美,问题解决。
这里要吐槽一下,array-union 这个库,其实就一行代码: constarrayUnion = (...arguments_) => [...newSet(arguments_.flat())];
就这还引入一个额外的库,坑!