npm 包生态系统如何与 Cloudflare 协同工作

最后更新: 01/02/2026
作者: C 源跟踪
  • npm 提供构建面向 Cloudflare workerd 运行时的项目所需的核心依赖项管理、版本控制和脚本编写功能。
  • workerd 与 Node.js 的区别在于,它专注于安全、符合 Web 标准的 API,因此需要兼容层来支持 Node 特有的模块。
  • 新的 nodejs_compat_v2 模式混合了原生 C++ 实现、unenv polyfill 和 mock,从而大大提高了对 npm 包的支持。
  • 模块别名和选择性 polyfill 允许您自定义不兼容依赖项的行为,并在 Workers 上解锁更多 npm 生态系统的功能。

npm 工作包概念

将 npm 生态系统与 Cloudflare 的 workerd 运行时相结合 听起来可能有点神秘,但其本质是让 Node.js 风格的代码和包在以 Web 为中心的平台上流畅运行。Cloudflare Workers 和 Pages 现在提供了一个改进的 Node.js 兼容层,让您可以引入更多 npm 包,而无需费力处理不同运行时之间的底层差异。

本文解释了 npm 包如何与 workerd 以及新的兼容性标志相互作用。解释了为什么有些软件包过去会失败,以及随着……发生了哪些变化 nodejs_compat_v2 模式。您还将了解 npm 的行为(安装、更新、脚本和依赖类型)如何融入以 workerd 为目标的项目中,以便您可以自信地构建应用程序并避免意外情况。

npm 是什么?它对 workerd 有什么重要意义?

npm 仍然是 Node.js 的事实上的包管理器。它为服务器端代码和当今大部分前端工具提供支持。它最初​​只是一个简单的依赖管理器,但很快发展成为一个通用的注册表和命令行界面,几乎每个 JavaScript 开发人员每天都会用到它。

npm 注册表包含数百万个软件包。这意味着几乎任何需求都能找到相应的库:HTTP 客户端、身份验证、数据库驱动程序、构建工具、测试框架等等。对于 workerd 和 Cloudflare Workers 来说,这个生态系统既是机遇也是挑战:你可以使用许多工具,但其中很多工具都是基于 Node.js 运行时环境而非 Web 标准环境构建的。

npm 对于前端工作流程同样至关重要。其中,打包工具、转译器和代码检查器作为开发时依赖项安装。无论是构建 React 单页应用程序 (SPA) 还是在 workerd 上运行的 Worker 脚本,您都可能使用 npm(或 Yarn/pnpm)来管理依赖项和脚本。

npm 的核心功能是自动化安装、更新和依赖项跟踪。保持模块在 node_modules 以及记录要求 package.json对于基于 workerd 的 Workers,您的 npm 设置看起来很熟悉,但执行您的代码的运行时是 workerd 引擎,而不是 Node.js 本身。

Yarn 和 pnpm 等替代方案提供了不同的命令行界面和性能特性。但是,当目标是 workerd 时,概念是一样的:包管理器解析模块,而 Cloudflare 的构建工具和兼容性标志决定这些模块如何在 Worker 运行时中执行。

npm 的依赖项安装工作原理

npm 集成

运行标准的 npm install 命令会填充您的 node_modules 通过读取依赖项列表,并引入每个列出的软件包及其传递依赖项,这样您就无需手动查找嵌套需求。

要添加新库,通常只需运行一个安装命令。并且自 npm 5 起,它会自动添加到 dependencies 部分 package.json 除非你更改该行为。

npm 支持一些标志,用于对包在项目中的使用方式进行分类。这在针对 workerd 等运行时环境时非常有用,因为你可能需要不同的捆绑包或构建流程:

  • --save-dev 将该软件包添加到 devDependencies将其标记为仅在开发或构建步骤(例如测试运行器或打包器)期间需要。
  • --no-save 无需修改即可安装 package.json方便进行快速实验或一次性命令。
  • --save-optional 将包裹放入 optionalDependencies因此,即使安装失败,也不会中止整个过程。
  • --no-optional 阻止安装可选依赖项,从而减少资源占用或避免在某些平台上安装有问题的可选软件包。

当中的差异: dependenciesdevDependencies 为工人建造房屋时需要考虑的因素因为通常只有运行时依赖项需要打包和分发;开发依赖项在构建过程中会被剥离,从而保持部署规模较小。

可选依赖项为您提供了灵活的故障处理方式。但是,你的代码必须先检查它们是否可用,然后再依赖它们。这有助于解决软件包在 Node.js 和 workerd 中使用不同实现,或者在原生模块不受支持时回退的问题。

管理 npm 项目中的更新和版本

npm 的 update 命令会根据你声明的语义版本范围升级软件包。扫描已安装的模块,并将其更新为允许的最新版本,包括直接依赖项和嵌套依赖项。

您也可以根据需要更新单个软件包。如果某个库发布了影响 Worker 或其与非 Node 运行时(如 workerd)兼容性的错误修复或改进,则此方法非常有用。

npm 遵循语义化版本控制,使您可以精确控制升级边界。当你的工作进程依赖于与特定主版本绑定的库,或者上游引入了重大变更时,这一点至关重要。

锁定版本和使用锁定文件可以确保构建过程的可复现性。这样,团队和 CI 环境就能在本地开发、测试和生产工作节点之间生成相同的依赖关系图。

基于工作进程的工作流中的 npm 脚本和自动化

这个 scripts 场在 package.json 担任你的脚本运行器。允许您将短名称映射到更长的 CLI 命令并执行它们 npm run <script-name>.

现代项目使用 npm 脚本来封装构建工具、测试和打包程序。并且面向 workerd 的 Worker 项目通常会以这种方式公开打包、类型检查和部署命令。

一种常见的模式是通过脚本入口来连接打包器或任务运行器。将复杂的 CLI 调用转换为整个团队都可以访问的简单命令。

当与 Node.js 的 workerd 兼容性标志结合使用时,脚本功能将变得非常强大。因为脚本可以控制哪些兼容性选项处于活动状态,以及在打包最终 Worker 之前应用哪些 polyfill 或别名。

workerd 与 Node.js:了解运行时差异

workerd 是一个开源的 JavaScript 和 WebAssembly 引擎,针对边缘执行进行了优化。它基于 V8 构建——V8 是 Node.js 和 Chromium 使用的同一底层引擎——但其设计运行条件和信任模型有所不同。

Node.js 的创建目的是为了在宿主操作系统上运行 JavaScript,并提供强大的系统 API。process, fs 以及底层加密实用程序,使其成为服务器、CLI 和具有直接机器访问的后端基础设施的理想选择。

workerd 专为在多租户边缘进程中运行不受信任的代码而优化。强调隔离和以 Web 为中心的 API,例如 fetch流和 Cloudflare 特有的绑定(KV、Durable Objects、内部 RPC),而不是文件系统或进程访问。

为了提高互操作性,Cloudflare 协助建立了 WinterCG。旨在使服务器端 JavaScript 运行时和 Web 平台围绕一套通用 API 集保持一致,以便应用程序在不同环境下表现相似。

然而,许多 npm 包假定运行在 Node.js 环境中,并导入内置模块。 喜欢 events, fs, net, crypto or buffer如果没有兼容层,这些导入可能会失败,因为 workerd 不会自动提供 Node 特定的模块。

从 polyfill 到 Workers 中的内置 Node.js API

Cloudflare 最初依靠 polyfill 来连接 Node.js 和 workerd。使用 JavaScript 实现来模仿 Node API。2021 年,Workers 获得了基于 polyfill 的兼容模式,Wrangler 开始在运行时注入这些 polyfill。 node_compat = true 被设置在 wrangler.toml.

通过 node_compat = trueWrangler 为几个核心 Node 模块打包了 JS 实现利用插件,例如 @esbuild-plugins/node-globals-polyfillrollup-plugin-node-polyfills 因此,诸如此类的进口 import EventEmitter from 'events' 可以从事工人相关工作。

Polyfill 使得许多 npm 包能够在 Worker 上运行,但它也存在一些限制。特别是对于执行大量二进制或加密工作的模块而言,原生实现比纯 JS shim 快得多、准确得多。

Buffer 这是一个典型的例子,说明在用户层面上很难有效地模拟某些功能。因为复制和编码转换等操作可以从优化的原生实现中受益。这同样适用于 API,例如 CryptoAsyncLocalStorage.

为了提升性能和完整性,Cloudflare 开始将一些 Node API 嵌入到运行时环境中。 2023年通过 nodejs_compat 标志;这些核心模块是用 C++ 实现的,并提供给 Workers,以获得比 JS polyfill 更好的保真度。

在 Workers 中使用内置 Node 模块时,应该使用以下方式导入它们: node: 字首, 例如 import { Buffer } from 'node:buffer'表明依赖于运行时提供的模块,而不是注册表包。

为什么许多 npm 包在早期 nodejs_compat 版本下仍然无法正常工作

nodejs_compat 仍然会导致失败,因为许多库使用了没有前缀的导入语句。,例如, import { EventEmitter } from 'events'打包程序将这些模块视为文件系统模块,当这些模块不存在时,打包程序无法解析它们。

导入驱动程序时经常出现以下错误: pg 依赖于未加前缀的核心模块导致构建步骤报错说找不到该模块,即使 Node 将其视为内置模块。

开发者面临着原生 API 支持不足和速度较慢、功能不全的 polyfill 集之间的权衡。此外,还缺少全局变量,例如 process 许多库都认为该对象会存在于全局对象中。

这种摩擦使得在 workerd 上可靠地使用复杂的 npm 包变得困难。尤其是当间接依赖项需要特定的 Node 模块或全局变量时,会导致 Worker 运行之前出现构建时失败。

新的 nodejs_compat_v2:更好地支持 workerd 上的 npm

nodejs_compat_v2 将原生实现与按需 polyfill 相结合通过决定何时使用 C++ 支持的模块、JS polyfill 或轻量级存根,使导入能够成功,从而使 npm 生态系统在 Workers 上可用得多。

通过添加以下内容启用此模式 compatibility_flags = ["nodejs_compat_v2"]wrangler.toml这改变了运行时公开 Node API 的方式,以及 Wrangler 打包 Node 风格的导入和依赖项的方式。

许多之前导入失败的软件包现在在 v2 版本下可以正确加载。包括诸如以下的库: body-parser, jsonwebtoken, got, passport, knex 以及其他方面——减少构建时错误,转而为不支持的操作提供本地化的运行时反馈。

在 v2 版本中,你可以这样编写导入语句: import { Buffer } from 'buffer' 运行时会高效地路由它们。 到 C++ 支持的实现;同时,模块如 net 可以使用 Wrangler 进行 polyfill unenv让原生 API 和 polyfill API 能够共存而不发生冲突。

Wrangler 现在只会为 Worker 实际使用的 Node 模块注入 polyfill。通过分析代码和依赖项,而不是默认提供一整套 polyfill,从而保持包大小精简。

unenv polyfill 和模拟的 Node.js API

当没有原生实现或成熟的 polyfill 可用时, unenv 提供模拟模块 它们暴露相同的接口,但要么不执行任何操作,要么在调用不支持的方法时抛出描述性的运行时错误。

错误如下 [unenv] <method name> is not implemented yet! 更明确、更局部化让 Worker 启动,但仅在触发不兼容性的调用点失败,而不是在构建时中止。

模拟模块允许部分依赖于 Node 特性的软件包仍然可以被导入和使用。只要避开不支持的部分;安全的部分可以运行,而依赖于文件的操作只有在执行时才会触发错误。

此前,任何进口 fs 可能导致 Workers 中的包无法使用,但与 nodejs_compat_v2 unenv 可以模拟依赖项,并选择性地包含和调用它。

从构建时反馈到运行时反馈的这种转变简化了调试过程。因为您可以准确地识别出哪个方法和调用堆栈触发了不兼容性,然后重构代码或提供有针对性的 polyfill 或别名作为解决方法。

模块别名:自定义问题依赖项的行为

模块别名允许你将导入重定向到你自己的实现。,配置 wrangler.toml因此,有问题的模块路径会解析为自定义文件,而不是默认行为。

如果一个库依赖于 fs.readFile 但你不需要文件系统访问权限,别名 "fs"./fs-polyfill 并公开自定义 readFile 记录日志、调用其他 API 或从 KV 读取数据。

设置别名后,导入方式如下: import { readFile } from 'fs' 解析到你的模块并绕过 unenv 的默认值防止出现“尚未实现”错误,同时保持使用包不变。

当依赖项引入 Node 特有的软件包时,别名也很有用,例如 node-fetch这可能依赖于不受支持的 Node 模块;您可以进行映射。 "node-fetch" 到一个重新导出全局变量的模块 fetch.

像工具一样 nolyfill 简化重新导出模式这样,您就可以绕过不兼容的实现,并保持依赖包在 workerd 上运行。

模块别名充当了其上的一个灵活的兼容层。 nodejs_compat_v2允许您在不重写或派生的情况下修改特定软件包。

绩效、生态系统协作和推广

workerd 内部用 C++ 原生实现的关键 Node.js API 可提供更好的性能和正确性。以及类似这样的模块 Buffer, AsyncLocalStorageCrypto 利用这些封装在 JS shim 中的原生实现,可以模拟 Node 的行为。

Cloudflare 为……做出了贡献 unenv它提供智能的、按需的聚合物填充和模拟因为它面向多个运行时环境,并且已被 Nuxt 和 Nitro 等项目采用;仅添加必要的 polyfill 可以保持应用程序轻量级,并促进生态系统融合。

更广泛的目标是实现Node风格代码在不同运行时之间的可移植性。这样,开发者只需编写一次代码,即可在 Node.js、workerd 或其他环境中运行,并且可以根据使用情况自动选择 polyfill 和原生功能,从而最大限度地减少摩擦。

行为的改善 nodejs_compat_v2 预计随着时间的推移,它将成为默认值 当您的 Worker 的兼容性日期足够新时,更多的 Worker 将透明地受益于更强的 npm 兼容性,而无需额外的配置。

我们鼓励开发者尝试改进后的 Node.js 兼容性并更新他们的 wrangler.toml通过反馈渠道报告剩余的不兼容或错误,以便弥补差距。

npm成熟的依赖管理、workerd安全的以Web为中心的运行时以及不断发展的Node.js兼容层的结合。 它为您提供了一条切实可行的途径,让您能够重用大量现有的 JavaScript 代码,同时充分利用边缘执行、隔离和 Cloudflare 的平台特性。借助智能 polyfill、原生实现、模拟和模块别名等功能,您可以轻松地将复杂的 npm 包集成到 Workers 项目中,而无需频繁地与运行时环境进行交互。

相关文章: