Node.js 是一个开源、跨平台的 JavaScript 运行时环境,它让开发人员能够创建服务器、Web 应用、命令行工具和脚本

  • 在浏览器外部运行 V8 JavaScript 引擎,这使得 Node.js 具有非常高的性能
  • Node.js 使用异步 I/O 和事件驱动的设计理念,提供了非阻塞式 I/O 接口和事件循环机制(由 libuv 实现),使得开发人员可以编写高性能、可扩展的应用程序
  • Node.js 适合做一些 IO 密集型应用,不擅长 CPU 密集型应用
    • IO 依靠 libuv 有很强的处理能力
    • 而因为 Node.js 单线程原因,导致 CPU 利用率不高
    • 可以编写 C++ 插件或者 Node.js 提供的 cluster 模块使用多线程

  1. Node.js 标准库:由 JavaScript 编写,是平时我们经常引入的各个模块,如:httpfsrequest 等
  2. Node bingdings:Node.js 程序主函数入口,还有提供给 lib 模块的 C++ 类接口,这一层是 JavaScript 与底层 C/C++ 沟通的桥梁,由 C/C++ 编写
  3. 最底层,支持 Node.js 运行的关键,由 C/C++ 编写:
    • V8:用来解析、执行 JavaScript 代码的运行环境
    • libuv:提供最底层的 I/O 操作接口,包括文件异步 I/O 的线程池管理和网络的 I/O 操作,是整个异步 I/O 实现的核心

模块化

Node.js 同时支持 CommonJS 和 ES 模块系统(自 Node.js V12 起)

  • Node.js 提供的 CommonJS Module —— CJS
  • ECMAScript 提供的 ES Module —— ESM

Node.js 默认使用 cjs,如果使用 ES Module 需要将文件名改成 xxx.mjs

和浏览器的区别

宿主 API

  • 浏览器:与 DOM 或其他 Web API(如 Cookie)进行交互
  • Node.js:运行于操作系统

所以 Node.js 没有 documentwindow 等浏览器提供的 API,但有其他模块,如文件系统等

控制环境

  • 编写 Node.js 应用程序时,通常知道将在哪个版本的 Node.js 上运行,所以可以编写 Node.js 版本支持的所有现代 ES2015+ JavaScript
  • 而浏览器,访问者使用的浏览器可能五花八门,所以在将代码发送到浏览器之前,需要使用 Babel 等工具将代码转换为与 ES5 兼容

ECMAScript 新特性

ECMAScript 功能分为三组,分别是发布(shipping)、暂存(staged)、进行中(in progress)

  • 所有发布功能在 Node.js 上默认打开,不需要任何运行时标志
  • 阶段性(暂存)功能,即 V8 团队认为不稳定的即将完成的功能,需要运行时标志:--harmony
  • 进行中的功能可以通过各自的标志单独激活,除非出于测试目的,否则强烈建议不要这样做,因为极其不稳定

开发环境与生产环境

Node.js 中的开发和生产之间没有区别,即:无需应用任何特定设置即可使 Node.js 在生产配置中工作

但是很多 Node.js 库通过 NODE_ENV 环境变量,区分开发环境与生产环境,以进行不同逻辑处理。如 express 中,将 NODE_ENV 设置为 production 通常可以确保:

  • 日志记录保持在最低限度的水平
  • 进行更多的缓存级别以优化性能

注意:NODE_ENV 环境变量是外部库中广泛使用的约定,但 Node.js 本身并未使用

修改 NODE_ENV 环境变量的方法:

  1. 临时修改:export NODE_ENV=production
  2. 写入 shell 配置文件,如 Bash shell 的 .bash_profile 文件
  3. 添加到应用程序初始化命令中:NODE_ENV=production node app.js

Node.js 程序读取:

if (process.env.NODE_ENV === 'development') {
  app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
}
 
if (process.env.NODE_ENV === 'production') {
  app.use(express.errorHandler());
}

WebAssembly 支持

WebAssembly 是一种高性能的类汇编语言,可以从多种语言进行编译,如 C/C++、Rust、AssemblyScript 等。目前,Chrome、Firefox、Safari、Edge 和 Node.js 都支持它

WebAssembly 规范详细介绍了两种文件格式:

  1. WebAssembly 模块:扩展名为 .wasm 的二进制格式
  2. WebAssembly 文本格式:扩展名为 .wat 的文本表示形式

有多种方法可用于生成 WebAssembly 二进制文件,包括:

  • 手动编写 WebAssembly (.wat) 并使用 wabt 等工具转换为二进制格式
  • 使用 emscripten 编译 C/C++ 代码
  • 使用 wasm-pack 编译 Rust 代码
  • 使用 AssemblyScript(类似于 TypeScript)

其中一些工具不仅生成二进制文件,还生成 JavaScript “粘合”代码以在浏览器中运行

使用

Node.js 通过全局 WebAssembly 对象提供必要的 API

console.log(WebAssembly);
/*
Object [WebAssembly] {
  compile: [Function: compile],
  validate: [Function: validate],
  instantiate: [Function: instantiate]
}
*/
const fs = require('node:fs');
 
const wasmBuffer = fs.readFileSync('/path/to/add.wasm');
 
WebAssembly.instantiate(wasmBuffer).then(wasmModule => {
  const { add } = wasmModule.instance.exports;
  const sum = add(5, 6);
  console.log(sum); // 11
});

与操作系统交互

WebAssembly 模块本身无法直接访问操作系统功能。可以使用第三方工具 Wasmtime 来访问此功能,其利用 WASI API 来访问操作系统功能

TypeScript 支持

使用 tsc

npm i -D typescript
npx tsc example.ts
node example.js

使用 ts-node

npm i -D ts-node
npx ts-node example.ts

Node.js 原生支持

从 V22.6.0 开始,Node.js 对某些 TypeScript 语法提供了实验性支持。可以直接在 Node.js 中编写有效的 TypeScript 代码,而无需先进行转译

node --experimental-strip-types example.ts

--experimental-strip-types 标志告诉 Node.js 在运行 TypeScript 代码之前删除类型注释

Debug

node --inspect app.js

使用 --inspect 标记启用调试模式,Node.js 进程会侦听调试客户端。默认情况下,它将侦听 127.0.0.1:9229。每个进程还被分配了一个唯一的 UUID

Inspector 客户端必须知道并指定要连接的主机地址、端口和 UUID。完整的 URL 看起来像 ws://127.0.0.1:9229/0f2c936f-b1cd-4ac9-aab3-f63b0f33d55e