TypeScript 开发 Node.js 应用时如何处理 alias 和 esm
前一阵子学习了一点 TypeScript 并用 Vue + Vite 做了一个简单的 Demo,体验了一下 TS 的快感。由此想尝试下使用 TS 开发一下 Node.js 应用,由于前一段时间一直用 Vue.js 便很顺手的用上了 alias 和 import。 没想到事情并没有那么简单……
问题起因
从网上搜索和参考官方项目使用 TypeScript 开发 Node.js 应用的流程基本上是这样的:
对于开发调试: 使用 nodemon(检测文件变动) + ts-node 自动编译
对于构建生产: 使用 tsc 进行编译
但是,当我们使用 alias(即别名) 时,我们自然的在 tsconfig.json 上配置好 path、alias,却发现这样运行是有问题的:由于 TypeScript 在编译过程中并不会解析与替换我们设置的 alias (即别名),而同时编译后的 js 文件也自然不会被 node 解析,所以我们就会遇到对于 alias 路径的报错。 而这部分工作在 Web 开发中是由 Vite 或 Webpack 这类工具替我们处理的(当然,在这里你也可以引入它们,但是……)。
思路分歧:安能两全?
所以解决这个问题的思路就是找一些相关的 package 以及使用一些 hack 的手段来达成目的——解析 alias 路径。似乎这是很简单的——你可以很容易搜到一些这类的 package 比如:module-alias 、 tsconfig-paths、tsc-alias。但是当你想把这一些与 esm 结合时,却又出现了问题。
首先,module-alias
对 esm 并未支持,即使你想调整编译结果为 commonjs
,如果配合 ts-node
使用,仍然会出现一堆问题。
其次是 tsconfig-path
,如果我们使用 ts-node
运行一个 esm 文件,我们必须使用 node --loader ts-node/esm src/index.ts
,而使用 tsconfig-path
运行时,你则需要 ts-node -r tsconfig-paths/register src/index.ts
,那么我们怎么把两者结合呢? node --loader ts-node/esm -r tsconfig-paths/register src/index.ts
? 这在我的实际测试中似乎并不管用……最致命的是,他是运行于 运行时
(run-time) 的,这意味着它并不会影响你的编译结果,仅仅在你调试的时候生效。
最后是 tsc-alias
,他非常好用,但是与 tsconfig-path
相反,他仅会处理编译后的结果,这也意味着他没有办法很好的与 ts-node
和 nodemon
直接适用。
另外,我还发现 vite
搞了 vite-node 这个东西,配合 vite.config.ts
在运行时似乎能起到不错的效果,但是目前仍处在初级阶段,有许多问题,所以并不推荐使用。并目前还不支持 build。
开发与生产分开处理
那没有什么很好的办法去解决这个问题了吗?答案是:确实。我没有发现很好的方法去解决这个问题。许多时候你确实要选择性的舍弃一些东西。 比如编译结果要选择为 commonjs
而非 esnext
回顾我们的开发的流程:调试与生产。这两个阶段如果分别适用不同的方法,似乎能达到不同的效果。
由于 tsc-alias
可以直接对 生产后的文件处理 alias ,因此我们完全可以把他设置在 scripts 的 build 中,即 tsc && tsc-alias
。 其他部分则分别选择上面的与 nodemon 进行结合(当然不包括 module-alias)
于是我对以上的 package 测试后,发现以下三种方法,你也可以直接去 Github 上看这个 repo。
他们的 package.json 基本都是一样的,为了方便我们使用 nodemon.json 来配置执行的指令,scripts 部分参考:
"scripts": { "dev": "nodemon", "build": "tsc && tsc-alias" } 复制代码
第一种:
开发使用:nodemon & ts-node & tsconfig-paths
构建使用:tsc & tsc-alias
这个的缺点就是你必须使用 commonjs 作为编译目标。
nodemon.json
{ "watch": ["src"], "ext": "ts,json", "ignore": ["src/**/*.spec.ts"], "exec": "ts-node -r tsconfig-paths/register src/index.ts" } 复制代码
第二种:
开发使用:nodemon & tsc & tsc-alias
构建使用:tsc & tsc-alias
本质上就是使用 nodemon 监测文件变动,编译、解析 alias,并运行编译结果。
缺点是 tsc 要稍微慢点。
nodemon.json
{ "watch": ["src"], "ext": "ts,json", "ignore": ["src/**/*.spec.ts"], "exec": "tsc;tsc-alias;node lib/index.js" } 复制代码
第三种:
开发使用:nodemon & vite-node
构建使用:tsc & tsc-alias
缺点就是前面说,vite-node
尚处于早期阶段,并且开启一个 vite 服务也会带来额外功耗。
nodemon.json
{ "watch": ["src"], "ext": "ts,json", "ignore": ["src/**/*.spec.ts"], "exec": "yarn vite-node src/index.ts" } 复制代码
具体代码你可以去 Github 上自己 clone。
思考
以上是目前阶段的解决问题,但很明显这不是最终方案。其实问题的本质是 node.js 对于 esm 的支持程度;即由于 commonjs 的 require 和 import 的本质不同导致的。但是随着其发展应该是能够得到优化与解决的。
作者:张麦麦
链接:https://juejin.cn/post/7047953671488798734