TypeScript生成ES6的JS
我在使用TypeScript过程中有一个疑问,编写的是现代化的js可以用上:在TSconfig.json中
{
"compilerOptions": {
"target": "ES6",
"module": "ES6",
}
}
TS代码
import {add, sub} from './mod1';
let a = 10;
const b= 20;
let c = add(a, b);
console.log(c)
let d = sub(b, a);
console.log(d)
const sub = function(a:number, b:number):number {
return a - b;
}
const add = function(a:number, b:number):number {
return a+b;
}
export { sub ,add };
生成出的ES6的JS是
import { add, sub } from './mod1';
let a = 10;
const b = 20;
let c = add(a, b);
console.log(c);
let d = sub(b, a);
console.log(d);
const sub = function (a, b) {
return a - b;
};
const add = function (a, b) {
return a + b;
};
export { sub, add };
可以看出两者几乎是一样的。此时我们执行tsc && node ./dist/main.js时会发现无法执行,提示如下错误:
node:internal/errors:484
ErrorCaptureStackTrace(err);
^
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/xxxx/dist/mod1' imported from /Users/pengxu/Work/Code/hello/hello/dist/main.js
at new NodeError (node:internal/errors:393:5)
at finalizeResolution (node:internal/modules/esm/resolve:305:11)
at moduleResolve (node:internal/modules/esm/resolve:866:10)
at defaultResolve (node:internal/modules/esm/resolve:1074:11)
at nextResolve (node:internal/modules/esm/loader:163:28)
at ESMLoader.resolve (node:internal/modules/esm/loader:838:30)
at ESMLoader.getModuleJob (node:internal/modules/esm/loader:425:18)
at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:76:40)
at link (node:internal/modules/esm/module_job:75:36) {
code: 'ERR_MODULE_NOT_FOUND'
}
这是什么原因呢?经过研究发现,typescript的import与js的import是不同的:
ts的import不需要完整的文件名:import {add, sub} from ‘./mod1’;
es6的js的需要完整的文件名:import { add, sub } from ‘./mod1.js’;
经过修改后可以运行了。
但是tsc生成的文件不可能一个个的去修改吧。那怎么办呢?又经过研究发现,node有一个选项是允许不带后缀名import的:
node --experimental-specifier-resolution=node ./dist/main.js
其中–experimental-specifier-resolution=node是node的一个实验性选项,开启后执行时会提示一个警告
(node:51193) ExperimentalWarning: The Node.js specifier resolution flag is experimental. It could change or be removed at any time.
吐槽:也许后面的nodejs版本就会不再显示这个警告了;也许后面的nodejs版本不允许这么干了,鬼知道。我想这就是为什么都node18代了,网上的教程还是在教怎么写require,用es5的方式写代码。nodejs本身对module的加载方式太乱了。兼容性太差,坑太多。
加上了这个选项后再执行,发现报这个错误:
(node:51193) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension.
import { add, sub } from './mod1';
^^^^^^
SyntaxError: Cannot use import statement outside a module
at Object.compileFunction (node:vm:360:18)
at wrapSafe (node:internal/modules/cjs/loader:1048:15)
at Module._compile (node:internal/modules/cjs/loader:1083:27)
at Module._extensions..js (node:internal/modules/cjs/loader:1173:10)
at Module.load (node:internal/modules/cjs/loader:997:32)
at Module._load (node:internal/modules/cjs/loader:838:12)
at ModuleWrap.<anonymous> (node:internal/modules/esm/translators:170:29)
at ModuleJob.run (node:internal/modules/esm/module_job:193:25)
at async Promise.all (index 0)
at async ESMLoader.import (node:internal/modules/esm/loader:527:24)
这个问题的原因是一直以来NODEJS都是用CommonJS的方式(就是require(‘xxxx’)和module.expores={})这种方式导入导出模块。这种方式是同步的,顺序执行的。而es6里的import的区别就大了(根本就是两套逻辑)import是异步的,预加载的。在nodejs进化的历史长河中,花了很多很多年,都没有统一这两种加载方式,历史包袱太重。(后来就有了Deno,这是另一个话题)导致了一代代教程教出来的的程序员前赴后继的写了require。(import可以使用require)。但是就在这个问题还没有搞定的时候,typescript又降维打入进来,我看到很多前端或全栈小哥,在三套标准(commonjs ,es6, ts)之前迷离。写着或机械的,或玄学代码。
扯远了,说回来以上错误的处理方法很简单,在package.json里把type设为module,这是nodejs的配置,如果不加这个时在nodejs遇到.js 或.cjs 文件时会使用CommonJS方式,如果遇到.mjs 时用ES6 import加载。(这样就需要在文件扩展名层面上确定是哪种,貌似没有哪个语言是这样的,历史的坑)。还有一种方便的方法就是在package.json中使用type来指定用commonjs还是module方式来处理所有的js文件。
改造后的package.json如下:
{
"main": "./dist/main.js",
"type": "module",
"scripts": {
"dev": "tsc && node --experimental-specifier-resolution=node .",
}
}
此时就可以正常执行了。
|