Node.js 运行 TS/JS(ESM/CJS)
10

在 Node.js 中运行代码

Node.js 是一个允许你在服务器端运行 JavaScript 的运行时环境。随着 TypeScript 的流行,Node.js 生态也提供了成熟的方案来直接运行 TypeScript 代码。

1. 运行 JavaScript (.js 文件)

这是最基本的操作。假设你有一个名为 app.js 的文件,你只需要在终端中执行以下命令:

Bash

node app.js

Node.js 会直接解释并执行该文件中的 JavaScript 代码。

2. 运行 TypeScript (.ts 文件)

Node.js 本身无法直接识别 TypeScript 语法。因此,我们需要借助一些工具将 TypeScript "实时"地转换为 JavaScript 来执行,或者先编译再执行。

方法一:使用 ts-node (推荐用于开发环境)

ts-node 是一个广受欢迎的工具,它可以在内存中将 TypeScript 即时编译为 JavaScript 并直接由 Node.js 执行,省去了手动编译的步骤,极大地提升了开发效率。

步骤:

  1. 安装依赖:

    Bash

    # 在你的项目中安装 typescript 和 ts-node
    npm install -D typescript ts-node
    

    typescriptts-node 的对等依赖 (peer dependency),必须安装。

  2. 创建 tsconfig.json 文件:

    ts-node 依赖 TypeScript 的配置文件来正确地编译代码。你可以通过以下命令生成一个默认的配置文件:

    Bash

    npx tsc --init
    

    这会创建一个 tsconfig.json 文件,你可以根据项目需求进行修改。对于 Node.js 项目,建议将 "module" 选项设置为 "NodeNext""CommonJS"

  3. 运行 .ts 文件:

    假设你有一个 server.ts 文件,运行它就像运行 .js 文件一样简单:

    Bash

    npx ts-node server.ts
    

方法二:编译后运行 (推荐用于生产环境)

在生产环境中,为了获得最佳性能并减少不必要的依赖,标准的做法是先将所有 TypeScript 代码编译成 JavaScript,然后再用 Node.js 运行编译后的文件。

步骤:

  1. 安装 TypeScript:

    Bash

    npm install -D typescript
    
  2. 配置 tsconfig.json:

    确保 tsconfig.json 文件中的 outDir 选项被设置,它指定了编译后 .js 文件的输出目录。例如:

    JSON

    {
      "compilerOptions": {
        "target": "es2020",
        "module": "NodeNext",
        "outDir": "./dist", // 指定输出目录为 dist
        "strict": true
      }
    }
    
  3. 编译项目:

    在终端中运行 TypeScript 编译器:

    Bash

    npx tsc
    

    tsc 会读取 tsconfig.json 的配置,并将项目中的所有 .ts 文件编译成 .js 文件,存放在 ./dist 目录下。

  4. 运行编译后的文件:

    现在,你可以用标准的 Node.js 命令来运行你的应用了:

    Bash

    node dist/server.js
    

JavaScript 模块化语法:ESM vs. CJS

在 Node.js 中,如何组织和共享代码是通过模块系统来实现的。历史上,CommonJS 是 Node.js 的原生模块系统。后来,ECMAScript 标准定义了自己的模块系统,即 ES Modules。现代 Node.js 版本已经同时支持这两种系统。

1. CommonJS (CJS) - 传统模块系统

CJS 是 Node.js 诞生之初就采用的模块规范,它在服务端得到了广泛应用。它的模块加载是 同步 的。

核心语法:

  • 导出模块 (module.exports 或 exports)

    使用 module.exports 对象来导出一个模块的所有成员。

    JavaScript

    // math.js
    const PI = 3.14;
    
    function add(a, b) {
      return a + b;
    }
    
    class Calculator {
      // ...
    }
    
    // 导出一个包含多个成员的对象
    module.exports = {
      PI,
      add,
      Calculator
    };
    
    // 或者直接导出一个函数或类
    // module.exports = add;
    

    exportsmodule.exports 的一个引用或快捷方式。你可以用 exports.add = ...,但不能直接给 exports 赋值(如 exports = ...),因为这会切断它与 module.exports 的关联。

  • 导入模块 (require)

    使用 require() 函数来导入一个模块。require 会同步地加载并执行模块文件,然后返回其 module.exports 对象。

    JavaScript

    // app.js
    const math = require('./math.js'); // 导入本地模块
    const fs = require('fs'); // 导入内置模块
    
    console.log(math.PI); // 3.14
    console.log(math.add(2, 3)); // 5
    

特点:

  • 同步加载: require() 会阻塞后续代码的执行,直到模块加载完成。这在服务端是可行的,因为文件通常在本地磁盘,读取速度很快。

  • 动态导入: require 的路径可以是变量,可以在代码的任何位置调用。

  • 缓存: 模块在第一次被 require 后会被缓存,后续的 require 会直接从缓存中读取。

  • .cjs 文件: 在明确需要使用 CommonJS 的项目中,可以将文件后缀名改为 .cjs

2. ES Modules (ESM) - 官方标准模块系统

ESM 是由 ECMAScript 官方定义的标准模块化方案,它在浏览器和现代 Node.js 中都是首选。它的设计目标是 静态可分析,以支持摇树优化 (Tree Shaking) 等高级功能。

启用 ESM:

要在 Node.js 中使用 ESM,你有两种主要方式:

  1. 在 package.json 中声明:

    这是最常见的方式。在 package.json 文件中添加顶级字段 "type": "module"。

    JSON

    {
      "name": "my-esm-project",
      "version": "1.0.0",
      "type": "module"
    }
    

    一旦设置,项目中的所有 .js 文件都会被 Node.js 当作 ESM 模块来处理。

  2. 使用 .mjs 文件扩展名:

    如果你不想修改 package.json,或者想在同一个项目中混合使用 CJS 和 ESM,你可以将需要作为 ESM 处理的文件命名为 .mjs 后缀。

核心语法:

  • 导出模块 (export)

    使用 export 关键字。它可以用于命名导出(Named Exports)和默认导出(Default Export)。

    JavaScript

    // logger.mjs
    
    // 命名导出
    export const version = '1.0';
    
    export function log(message) {
      console.log(message);
    }
    
    // 默认导出 (每个模块最多一个)
    export default class Logger {
      constructor(name) {
        this.name = name;
      }
    }
    
  • 导入模块 (import)

    使用 import 关键字从其他模块导入成员。

    JavaScript

    // main.mjs
    import Logger, { version, log as printLog } from './logger.mjs'; // 导入默认和命名成员,并可以重命名
    
    console.log('Version:', version); // Version: 1.0
    printLog('Hello ESM!'); // Hello ESM!
    
    const myLogger = new Logger('main');
    

特点:

  • 异步加载: ESM 的加载和解析是异步的,更适合网络环境(如浏览器)。

  • 静态结构: importexport 必须在文件的顶层作用域使用,不能在条件语句或函数中。这种静态性使得工具可以在不执行代码的情况下分析模块依赖关系。

  • 导入路径: 导入本地文件时必须包含完整的文件名和扩展名(例如 ./logger.mjs),或者目录索引(./lib/)。

  • 顶层 await: 在 ESM 模块中,你可以在顶层直接使用 await 关键字,而无需将其包裹在 async 函数中,这对于异步初始化非常方便。

总结对比

特性

CommonJS (CJS)

ES Modules (ESM)

关键字

require, module.exports, exports

import, export

加载方式

同步

异步

语法位置

动态,可在代码任何位置

静态,只能在顶层

默认支持

Node.js 传统默认

现代 Web 和 Node.js 标准

启用方式

默认或使用 .cjs

package.json"type": "module" 或使用 .mjs

this 指向

指向当前模块的导出对象

undefined

文件路径

可以省略 .js 扩展名

必须提供完整文件路径和扩展名

Node.js 运行 TS/JS(ESM/CJS)
http://localhost:8090/archives/wei-ming-ming-wen-zhang
作者
Administrator
发布于
更新于
许可