Node.js、TypeScript 和 ESM:不必太痛苦
我正在考虑从头开始一个 Node.js 项目,并且需要做出一个艰难的选择:使用 ESM 还是 CommonJS。
一些流行的软件包作者决定采用 ESM,再加上工具的不断成熟,强烈表明这是大多数生态系统最终将趋同的方向。
在本文中,我的目标是提出一种不仅快速而且还能利用广泛采用和值得信赖的工具的解决方案。
🙅🏻♂️🛑 免责声明:
此方法旨在兼容在 Node.js 中运行的应用程序,而非浏览器。在其他场景下,模块分辨率可能会发生很大变化。
使其成为 ESM
第一步是将项目变成 ES 模块。你只需要在你的.js 文件"type": "module"
中引入 .js 。 对我来说,一个烦人的细节(从 CommonJS 转换过来)是 ES 模块要求导入时必须包含文件扩展名,如下所示:package.json
import { hey } from './module-b.js'
我们将在下一节中看到如何改进它。
tsconfig.json
这是适合我的用例的 tsconfig 的最小版本。
{
"compilerOptions": {
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "bundler",
"target": "ESNext",
"isolatedModules": true,
"esModuleInterop": true,
"noEmit": true,
"allowImportingTsExtensions": true,
"outDir": "dist",
"lib": [ "esnext" ],
"types": [ "node" ],
"baseUrl": "./",
},
"exclude": [ "node_modules" ],
"include": [
"src/**/*.ts",
"bin/*.ts"
]
}
注意allowImportingTsExtensions
那里的定义。这将允许你像下面这样使用导入:
import { hey } from './module-b.ts'
汇编
我决定使用SWC将项目编译成 JavaScript,主要是因为它的速度很快。需要注意的是,SWC 不会进行类型检查。在需要类型检查的情况下,你仍然可以使用tsc
以下方法来实现:
{
"scripts": {
"build": "swc src --out-dir dist/src",
"build:ci": "tsc && npm run build"
}
}
为了使导入按预期工作,.swcrc
您需要以下配置文件:
{
"exclude": "node_modules/",
"jsc": {
"parser": {
"syntax": "typescript",
"topLevelAwait": true
},
"target": "esnext",
"baseUrl": ".",
"experimental": {
"plugins": [
[
"@swc/plugin-transform-imports",
{
"^(.*?)(\\.ts)$": {
"skipDefaultConversion": true,
"transform": "{{matches.[1]}}.js"
}
}
]
]
}
},
"module": {
"type": "nodenext"
}
}
实时重载
此功能也常被称为热重载,它允许监视应用程序文件的更改,并在发生更改时重新启动应用程序。我使用 SWC 和nodemon来执行此功能,结果非常快。虽然使用两种不同的工具似乎有些不方便,但我非常喜欢它,因为它可以实现单一职责的目标:SWC 只负责编译代码,而 nodemon 负责监视 JavaScript 文件并重新启动 Node.js 进程。
以下结果展示了服务器重启所需的平均时间,同时观察更改并使用示例存储库分别运行 10 次:
时间(平均) | |
---|---|
多伦多 | 260毫秒 |
SWC+nodemon | 124毫秒 |
这些数字仅衡量从确认变更到新流程成功启动的时间。
为什么不包含ts-node-dev
在这些基准测试中?
遗憾的是,它目前与ts-node 加载器ts-node-dev
不兼容。
运行 TypeScript 文件
有时你需要单独运行 TypeScript 文件。在 Node v20 上,我无法实现swc-node
这一点(如果你遇到了,请告诉我!),所以我决定使用tsx
。它运行良好,而且使用起来非常简单。
{
"scripts": {
...
"migrate": "node --import tsx bin/run-migrations.ts"
...
}
}
最后的想法
在 Node.js 上使用 TypeScript 并编译为 ESM 的复杂性远超乎人们的想象。需要调整的地方有很多,有些工具本该能用,但实际却不行。我创建了一个示例仓库,其中包含了本文提到的所有内容,方便大家参考:a0viedo/node-ts-esm-example
鏂囩珷鏉ユ簮锛�https://dev.to/a0viedo/nodejs-typescript-and-esm-it-doesnt-have-to-be-painful-438e