预加载(Preload)
VextJS 提供了 预加载(Preload) 机制,允许以下两类来源在 Node.js 模块加载之前执行脚本:
- 依赖包声明:npm 包在
package.json中声明vext.preload - 项目级目录:应用项目根目录下的
preload/
vext start / vext dev 会自动发现这些声明,并通过 --import 参数注入到子进程中。
为什么需要预加载?
某些工具(如 OpenTelemetry SDK)必须在应用代码加载之前完成初始化,才能正确 patch Node.js 内置模块(http、net、dns)和第三方库(MongoDB、pg、Redis 等)。
Node.js 的 --import 参数正是为此设计:它确保指定脚本在任何用户代码执行前运行。
手动添加 --import 需要修改启动脚本,增加了配置负担。VextJS 的 preload 机制将这一步自动化:
- 插件包只需在
package.json中声明vext.preload - 应用项目只需在项目根创建
preload/目录
CLI 会自动完成注入。
从
v0.3.6开始,应用项目无需再为了 preload 去包装一个本地 npm 包。只要在项目根创建preload/目录即可。
工作原理
时序图
声明 preload
方式 A:项目级 preload/ 目录
在应用项目根目录创建:
首期规则:
支持的文件类型
推荐优先使用
.mjs/.mts,语义最清晰。
TypeScript preload 的工作方式
如果项目级 preload/ 目录中包含 .ts / .mts 文件,CLI 会在启动前使用 esbuild 将其编译到:
例如:
这样做的目的,是在不改造 vext build 主编译链的前提下,同时保证:
vext devvext start- Cluster worker
三条链路的 preload 行为一致。
vext dev 下的行为
项目级 preload 属于启动前执行逻辑。因此当 preload/ 里的文件发生新增 / 修改 / 删除时:
vext dev会监听该目录- 并统一触发 cold restart
这能确保结果与手动重启一致,避免“preload 已改但开发服务器仍沿用旧注入结果”。
方式 B:依赖包 vext.preload
在 npm 包的 package.json 中添加 vext.preload 字段:
字段格式
路径相对于包根目录(node_modules/<package>/),由 CLI 自动解析为绝对路径。
真实示例
vextjs-opentelemetry 已内置此声明:
安装后,vext start / vext dev 自动注入 --import,OpenTelemetry SDK 在应用启动前完成初始化,MongoDB / pg / Redis 等自动 patch 生效。
适用场景
preload 与 bootstrap config provider 的边界
preload 和 src/config/bootstrap.ts 都发生在应用完全启动前,但职责不同:
推荐做法:
- APM / OpenTelemetry / monkey patch → 用
preload - 启动前桥接环境变量给 bootstrap provider → 也可以用
preload - 远程配置中心 / 启动期数据库配置主链 → 用
bootstrap config provider - 两者可以配合:preload 先准备 SDK、token cache 或环境变量,provider 再读取这些状态产出 patch
三种启动模式
推荐使用
vext start/vext dev,享受自动注入的便利。
Cluster 模式
在 Cluster 模式下,preload 脚本同样生效。CLI 通过 cluster.setupPrimary({ execArgv }) 将 --import 参数传递给所有 Worker 进程:
注意事项
安全行为
- 项目级目录为受控单目录:仅识别项目根
preload/,不递归扫描任意目录 - 仅扫描直接依赖:CLI 只读取项目
package.json的dependencies+devDependencies,不递归扫描子依赖 - 文件不存在时跳过:
vext.preload指向的文件不存在时,CLI 输出 warning 并跳过,不阻断启动 - 解析失败时降级:依赖包
package.json解析失败时静默跳过 - 项目级 TS preload 编译失败时 fail-fast:避免把明显不可执行的 TS preload 带进运行阶段
- 无 preload 声明时无影响:没有项目级目录、也没有包级 preload 声明时,CLI 行为与之前完全一致
与手动 --import 共存
CLI 注入的 --import 与用户手动添加的 --import 不冲突。如果同一脚本被注入两次,SDK 内部通常有全局注册保护,不会重复初始化。
开发 preload 脚本的建议
- 脚本应快速执行,避免阻塞应用启动
- 如果是
.js/.ts,请确保项目采用 ESM 语义("type": "module") - 错误应自行处理;若是 TS preload,语法编译错误会直接中断启动
部署边界
如果你使用的是项目级 preload/:
vext build会把项目根preload/编译到dist/preload/.ts/.mts/.js/.mjs都会统一输出为可直接--import的.mjs文件- 因此生产部署时,通常只需要一起携带:
- 项目根
package.json dist/(其中已包含dist/preload/,如被使用)
- 项目根
vext start 会优先读取项目根 preload/;若根目录不存在该目录,则自动回退读取 dist/preload/。
编写自定义 preload
编写项目级 preload
编写包级 preload
如果你正在开发一个需要 preload 的 vext 插件包:
在 package.json 中声明:
构建后,任何使用 vext start / vext dev 的项目安装此包后,preload 脚本会自动执行。
下一步
- 查看 OpenTelemetry 可观测性 了解 preload 的典型应用
- 了解 插件 系统的完整能力
- 探索 Cluster 多进程 模式下的 preload 行为