Node.js 使用 TypeScript 和 ES6 模块

2022/02/04 posted in  JS CSS HTML
Tags:  #JavaScript

背景

我们都知道 JS 模块化的演变经历了一个漫长的过程,从最初的 CommonJS,到后来的 AMD 和 CMD,再到今天的 ES6 模块化方案。优胜劣汰,对于 JS 这门语言来说,主要用于 Node.js 的模块化方案 CommonJS 活了下来,而 ES6 推出的模块化方案更是赢得了大家的认可,大有可能成为未来 JS 的主要模块化方案。

CommonJS 模块方案在 Node.js 很常用,但随着 Node.js 也开始支持 ES6 的模块化方案,不少第三方库转为了使用 ES6 模块方案,本文将介绍如何在 Node.js 上使用 TypeScript 和 ES6 模块。

给 package.json 添加 type 属性

Node.js 新增支持了 package.jsontype 属性,可以设置为 "module""commonjs",不设置则默认为 "commonjs",这里要设置为 "module"

{
    "name": "my-package",
    "type": "module",
    "//": "...",
    "dependencies": {
    }
}

设置为 module 时将 .js 文件视为 ES6 模块
设置为 commonjs 时将 .js 文件视为 CommonJS 模块

详细阅读:
ECMAScript Modules in Node.js

tsconfig.json 配置

{
  "compilerOptions": {
    "module": "node16",
    "moduleResolution": "node16",
    "strict": true,
    "removeComments": true,
    "preserveConstEnums": true,
    "outDir": "dist"
  },
  "ts-node": {
    "esm": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}
  1. module 表示我们在 ts 文件中引入模块时使用什么方式,这里设置为 node16

    使用相对路径引入自定义模块,需要添加后缀名,要注意,引入的文件是 ./foo.ts,但这里要写 .js 后缀,因为 ts 文件最后会编译为 js 文件

    // 引入第三方模块
    import fetch from "node-fetch";
    
    // 相对路径引入自定义模块
    import { helper } from "./foo.js";
    
  2. moduleResolution 表示寻找模块的方式,这里设置为 node16

  3. ts-node 配置 esm 属性,这样 ts-node 直接运行 ts 文件时可以正确处理 ES6 模块,如果不使用 ts-node,可以不设置

详细阅读:
Modules
Module Resolution

在 ES 模块中引入 CommonJS 模块

如果我们使用的第三方模块是 CommonJS 的方式编写的,那么在 ES6 模块中也可以引入 CommonJS 模块。

ES6 模块的 import 命令可以加载 CommonJS 模块,但是只能整体加载,不能只加载单一的输出项。

// 正确
import packageMain from 'commonjs-package';

// 报错
import { method } from 'commonjs-package';

这是因为 ES6 模块需要支持静态代码分析,而 CommonJS 模块的输出接口是 module.exports,是一个对象,无法被静态分析,所以只能整体加载。

加载单一的输出项,可以写成下面这样。

import packageMain from 'commonjs-package';
const { method } = packageMain;

详细阅读:
Node.js 如何处理 ES6 模块