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
模块使用多线程
- Node.js 标准库:由 JavaScript 编写,是平时我们经常引入的各个模块,如:
http
、fs
、request
等 - Node bingdings:Node.js 程序主函数入口,还有提供给
lib
模块的 C++ 类接口,这一层是 JavaScript 与底层 C/C++ 沟通的桥梁,由 C/C++ 编写 - 最底层,支持 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 没有 document
、window
等浏览器提供的 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
环境变量的方法:
- 临时修改:
export NODE_ENV=production
- 写入 shell 配置文件,如 Bash shell 的
.bash_profile
文件 - 添加到应用程序初始化命令中:
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 规范详细介绍了两种文件格式:
- WebAssembly 模块:扩展名为
.wasm
的二进制格式 - 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