在这篇博文中,我将解释你需要知道的一切,以便在Node.js上使用和制作本地ECMAScript模块。
基础知识
让我们假设我们正在通过TypeScript实现一个npm包my-package 。该包有以下文件结构:
my-package/
ts/
src/
main.ts
util/
errors.ts
dist/
tsconfig.json
- A线(
"module"):我们正在告诉TypeScript生成ECMAScript模块。"ES6","ES2015":支持基本的ESM功能"2020": 另外,支持动态导入和import.meta。
- B行(
"moduleResolution"):这个值是Node.js需要的。 - C行(
"allowSyntheticDefaultImports"):我需要这个设置,以便导入一个传统的CommonJS模块。在这种情况下,module.exports是默认的出口。
package.json
在package.json 中需要以下条目:
"type": "module"
指定一个"type" ,无论如何都是推荐的,但在这里是必须的,因为TypeScript还不支持.mjs ,只支持.js 。
如果这个包被npm安装在另一个包中,这就是该包导入函数的方式start():
import {start} from 'my-package/dist/src/main.js';
这里的关键点是,在默认情况下,我们需要提供文件名扩展,特别是对于同一包内的模块。
Visual Studio代码
默认情况下,VS Code在为我们添加导入时不会添加文件名扩展。这可以通过以下两个设置来改变:
"javascript.preferences.importModuleSpecifierEnding": "js"
"typescript.preferences.importModuleSpecifierEnding": "js"
对于一个包内的导入,你可以按以下方式添加文件名扩展名。
- 搜索。
^(import [^';]* from '(\./|(\.\./)+)[^';.]*)'; - 替换。
$1.js';
包出口:更好的模块指定器
包出口或 包的入口点让我们在导入时定义更好的模块指定符。回想一下,这就是包的文件结构:
my-package/
ts/
src/
main.ts
util/
errors.ts
dist/
包本身的一个入口点
package.json:
{
"main": "./dist/src/main.js",
"exports": {
".": "./dist/src/main.js"
},
"typesVersions": {
"*": {
"main.d.ts": ["dist/src/main.d.ts"]
}
}
}
我们只提供"main" ,以便向后兼容。
"typesVersions"执行与"exports" 相同的映射,但针对TypeScript的类型定义。
这就是TypeScript中的导入语句:
import {start} from 'my-package';
子树的更好的模块指定器
package.json:
{
"exports": {
"./*": "./dist/src/*"
},
"typesVersions": {
"*": {
"*": ["dist/src/*"]
}
}
}
在这里,我们缩短了整个子树的模块说明,在my-package/dist/src:
import {InternalError} from 'my-package/util/errors.js';
如果没有导出,导入语句将是:
import {InternalError} from 'my-package/dist/src/util/errors.js';
将一个目录的直接子目录导出为裸模块指定符
package.json:
{
"exports": {
"./util/*": "./dist/src/util/*.js"
},
"typesVersions": {
"*": {
"util/*": ["dist/src/util/*"]
}
}
}
只有./dist/src/util/ 的直接子目录被导出,所有的模块都可以作为裸模块指定符(无扩展)。
import {InternalError} from 'my-package/util/errors';
将一个目录映射到一个文件
package.json:
{
"exports": {
"./util": "./dist/src/util/errors.js"
},
"typesVersions": {
"*": {
"util": ["dist/src/util/errors.d.ts"]
}
}
}
在这里,我们想导出./dist/src/util/errors.js ,就像它所在的目录一样。
import {InternalError} from 'my-package/util';
高级软件包导出功能
Node.js支持更多的导出功能,例如 条件性导出:我们可以根据我们的包被导入的方式来改变我们的映射--例如,通过import 或通过require() (例子取自Node.js文档)。
{
"main": "./main-require.cjs",
"exports": {
"import": "./main-module.js",
"require": "./main-require.cjs"
},
"type": "module"
}
我们还可以区分浏览器和Node.js,以及更多。
浏览器呢?
到目前为止,我只在Node.js上测试了这个设置,但我认为捆绑器也会支持这些功能。如果还没有,那么最终会有。