每周博客 - 2025 年现代 Node.js 模式
自早期以来,Node.js 经历了显著的变革。如果你已经编写 Node.js 多年,你可能亲身见证了这一演变 —— 从充斥回调函数、由 CommonJS 主导的局面,到如今简洁、基于标准的开发体验。
这些变化不仅仅是表面上的,它们代表了我们处理服务器端 JavaScript 开发方式的根本性转变。现代 Node.js 拥抱 Web 标准,减少外部依赖,并提供更直观的开发者体验。让我们探讨这些转变,并理解它们为何对 2025 年你的应用程序至关重要。
1. 模块系统:ESM 成为新标准
模块系统或许是你会注意到最大差异的地方。CommonJS 曾很好地为我们服务,但 ES 模块(ESM)已成为明显的赢家,它提供了更好的工具支持并与 Web 标准保持一致。
旧方式(CommonJS)
让我们看看我们过去是如何构建模块的。这种方法需要显式的导出和同步的导入:
// math.js
function add(a, b) {
return a + b;
}
module.exports = { add };
// app.js
const { add } = require('./math');
console.log(add(2, 3));
这虽然可行,但存在局限性 —— 没有静态分析,没有树摇(tree-shaking),而且与浏览器标准不一 致。
现代方式(带有 Node: 前缀的 ES 模块)
现代 Node.js 开发采用 ES 模块,并添加了一个关键部分 —— 用于内置模块的 node:
前缀。这种显式命名避免了混淆,使依赖关系清晰明了:
// math.js
export function add(a, b) {
return a + b;
}
// app.js
import { add } from './math.js';
import { readFile } from 'node:fs/promises'; // 现代的 node: 前缀
import { createServer } from 'node:http';
console.log(add(2, 3));
node:
前缀不仅仅是一个约定 —— 它向开发者和工具发出了一个明确的信号,即你正在导入 Node.js 内置模块,而不是 npm 包。这防止了潜在的冲突,并使你的代码在依赖 关系方面更加明确。
顶级 Await:简化初始化
最具颠覆性的功能之一是顶级 await。不再需要为了在模块级别使用 await 而将整个应用程序包裹在一个异步函数中:
// app.js - 无需包装函数的简洁初始化
import { readFile } from 'node:fs/promises';
const config = JSON.parse(await readFile('config.json', 'utf8'));
const server = createServer(/\* ... \*/);
console.log('App started with config:', config.appName);
这消除了我们过去随处可见的立即调用异步函数表达式(IIFE)的常见模式。你的代码变得更具线性,也更容易推理。
2. 内置 Web API:减少外部依赖
Node.js 大力拥抱 Web 标准,将 Web 开发者已经熟悉的 API 直接引入到运行时中。这意味着更少的依赖和跨环境更高的一致性。
Fetch API:不再需要 HTTP 库依赖
还记得每个项目都需要 axios、node-fetch 或类似的库来进行 HTTP 请求的日子吗?那些日子已经一去不复返了。Node.js 现在原生包含了 Fetch API:
// 旧方式 - 需要外部依赖
const axios = require('axios');
const response = await axios.get('https://api.example.com/data');
// 现代方式 - 具有增强功能的内置 fetch
const response = await fetch('https://api.example.com/data');
const data = await response.json();
但现代方法不仅仅是替换你的 HTTP 库。你还获得了内置的复杂超时和取消支持:
async function fetchData(url) {
try {
const response = await fetch(url, {
signal: AbortSignal.timeout(5000) // Built-in timeout support
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return await response.json();
} catch (error) {
if (error.name === 'TimeoutError') {
throw new Error('Request timed out');
}
throw error;
}
}