Nuxt Kit 运行时配置 API 深度指南:useRuntimeConfig 与 updateRuntimeConfig 在模块开发中的实战应用
本篇文章面向 Nuxt 模块作者与进阶开发者,聚焦 Nuxt Kit 暴露的两个**构建期(build-time)**运行时配置工具——
useRuntimeConfig与updateRuntimeConfig。文章将结合@nuxt/kit的源码实现与测试用例,讲清二者在模块 setup 阶段如何读取「已被环境变量解析过」的配置、如何安全地合并注入新的配置项、以及在 Nitro 已初始化时如何触发配置热更新。读完你可以独立为自定义模块设计出规范、可被NUXT_环境变量覆盖的运行时配置方案。
定位:构建期的配置访问,而非运行期的配置读取
很多开发者会误将 Nuxt Kit 的 useRuntimeConfig 与应用内(Vue 组件、插件、服务端路由中)的 useRuntimeConfig() 混为一谈。二者的定位截然不同:
- 应用运行期调用的
useRuntimeConfig()是 Nuxt App 提供的 composable,用于在客户端 / 服务端运行时读取配置; - Kit 的
useRuntimeConfig()/updateRuntimeConfig()是模块开发工具,只在构建阶段(如模块setup回调、nuxt:ready钩子中等)有效,作用对象是nuxt.options.nitro.runtimeConfig这份已解析的构建期配置。
这一点在原文档(docs/4.api/5.kit/10.runtime-config.md)的开头有明确交代:"At build-time, it is possible to access the resolved Nuxt runtime config."。本文所述的两个函数均由 @nuxt/kit 从 packages/kit/src/runtime-config.ts 导出,其对外出口位于 packages/kit/src/index.ts(export { updateRuntimeConfig, useRuntimeConfig } from './runtime-config.ts')。
关于配置如何在 nuxt.config.ts 中声明、如何通过 NUXT_ 前缀环境变量覆盖、如何区分 public 与私有键等运行期机制,属于配套的 Nuxt Runtime Config 指南 范畴,下文只在涉及 Kit 行为差异时引用,不会喧宾夺主。
useRuntimeConfig:读取解析后的运行时配置
函数签名
function useRuntimeConfig (): Record<string, unknown>
它不接受任何参数,返回一份已应用环境变量覆盖的完整运行时配置对象。源码注释将其定位描述得非常清楚:"Access 'resolved' Nuxt runtime configuration, with values updated from environment. This mirrors the runtime behavior of Nitro."——即它在构建期复刻了 Nitro 在运行期的配置解析逻辑,保证模块作者看到的内容与最终部署时几乎一致。
源码级实现剖析
// packages/kit/src/runtime-config.ts
export function useRuntimeConfig (): Record<string, any> {
const nuxt = useNuxt()
return applyEnv(klona(nuxt.options.nitro.runtimeConfig!), {
prefix: 'NITRO_',
altPrefix: 'NUXT_',
envExpansion: nuxt.options.nitro.experimental?.envExpansion ?? !!process.env.NITRO_ENV_EXPANSION,
})
}
结合源码(packages/kit/src/runtime-config.ts)可以看到四个关键事实:
- 数据来源是 Nitro 的构建期配置:通过
useNuxt()拿到 Nuxt 实例后,读取nuxt.options.nitro.runtimeConfig。也就是说,任何模块、插件在此之前通过nuxt.options.nitro.runtimeConfig写入的内容都会被纳入返回结果。 - 返回的是深拷贝:结果先经
klona深克隆,避免你意外修改到 Nuxt 实例上的原始配置对象。 - 环境变量覆盖规则:会以
NITRO_为前缀、NUXT_为备选前缀,逐层查找并覆盖配置项——与 Nitro 运行期行为一致。例如配置键apiSecret会被NITRO_API_SECRET或NUXT_API_SECRET覆盖,嵌套键则用_连接(如public.apiBase对应NUXT_PUBLIC_API_BASE)。 - 环境变量展开可选:是否支持字符串内嵌
{{VAR}}展开取决于nitro.experimental.envExpansion,默认回退到读取进程环境变量NITRO_ENV_EXPANSION是否为真。
applyEnv:逐层覆盖的内部语义
环境变量的覆盖并非简单的"整对象替换",其内部递归逻辑(packages/kit/src/runtime-config.ts)值得模块作者注意,因为它决定了用户能以多细的粒度覆盖配置:
- 每个配置键先经
scule的snakeCase转为SCREAMING_SNAKE_CASE并拼接前缀,形成候选环境变量名,再读取NITRO_/NUXT_两个前缀下的同名变量; - 若当前值是对象且环境变量值也是对象(JSON 解析结果),则做浅合并后继续深入子键;
- 若当前值是对象但环境变量缺失,则继续向下递归子键;
- 若当前值是对象而环境变量提供了一个基本类型值,则该环境变量会覆盖整个对象分支并停止下钻;
- 对叶子键,只要有环境变量匹配就直接用其值替换默认值。
环境变量值的类型转换(destr 语义)
环境变量的原始字符串在读取时统一经过 destr 解析(packages/kit/src/runtime-config.ts),这意味着 JSON 兼容的字面量会被自动转型:"3000" 变成数字 3000、"true" 变成布尔 true、"null"/"undefined" 则落空。这一点在原文档中未展开,但测试文件 packages/kit/src/runtime-config.test.ts 用一张行为清单把它固化了:
| 环境变量原始值 | 读取后得到 |
|---|---|
hello-world |
'hello-world'(字符串) |
0 |
0(数字) |
3000 |
3000(数字) |
true / false |
true / false(布尔) |
null / undefined |
''(空字符串) |
4848e0 |
4848(数字,指数记法被解析) |
"4848e0"(带字面双引号) |
'4848e0'(字符串) |
若要强制保留字符串类型,需让环境变量值本身携带字面双引号(如 NUXT_MY_VAR='"4848e0"'),这与官方 Runtime Config 指南 中的 destr 警告一致。
实验性:字符串内的 {{VAR}} 展开
当 envExpansion 开启时,applyEnv 会对仍为字符串的最终值执行正则 /\{\{([^{}]*)\}\}/g 替换(packages/kit/src/runtime-config.ts),即 apiUrl: '{{BASE_URL}}/api' 会随环境变量 BASE_URL=http://example.com 解析为 http://example.com/api。
测试 packages/kit/src/runtime-config.test.ts 同时验证了两个重要边界:
- 展开只做单遍——若展开出的值里又含
{{Y}},不会递归展开第二次; - 当不存在匹配的环境变量时,
{{...}}原样保留,不会报错。
updateRuntimeConfig:合并更新构建期配置
函数签名
function updateRuntimeConfig (config: Record<string, unknown>): void | Promise<void>
该工具用来增量更新运行时配置。注意返回值既可能是 void 也可能是 Promise<void>,因此在使用时建议通过 await 或 .catch 处理潜在异步场景,具体取决于 Nitro 是否已初始化。
源码级实现剖析
// packages/kit/src/runtime-config.ts
export function updateRuntimeConfig (runtimeConfig: Record<string, unknown>): void | Promise<void> {
const nuxt = useNuxt()
Object.assign(nuxt.options.nitro.runtimeConfig as Record<string, unknown>, defu(runtimeConfig, nuxt.options.nitro.runtimeConfig))
try {
return useNitro().updateConfig({ runtimeConfig: runtimeConfig as any })
} catch {
// Nitro is not yet initialised - we can safely ignore this error
}
}
结合 packages/kit/src/runtime-config.ts,其行为可拆解为三层:
defu默认合并,而非覆盖:传入的runtimeConfig会以"新值优先"的方式合并进已存在的nuxt.options.nitro.runtimeConfig(defu不会用undefined覆盖已有值),然后通过Object.assign写回 Nuxt 实例上的 Nitro 配置。这意味着你可以只传模块关心的键,其余键保持用户配置不变。- 联动 Nitro 配置热更新:若 Nitro 实例已经可用,会调用
useNitro().updateConfig({ runtimeConfig })把变更同步给 Nitro。原文档特别指出——当 Nitro 已初始化时,这会触发一次 HMR 事件来重载 Nitro 的运行时配置,使开发模式下模块对配置的修改能即时生效。 - 对"尚未初始化"的容错:如果 Nitro 尚未初始化(例如在非常早期的构建阶段调用),
useNitro()抛出的错误会被捕获并静默忽略,配置仍已写入nuxt.options,稍后 Nitro 初始化时自然生效。useNitro()的使用前提(仅在ready钩子之后可调用)详见 packages/kit/src/nitro.ts。
需要说明的是,官方规范更推荐在 nuxt.options.runtimeConfig(或其 public 分支)上直接操作;updateRuntimeConfig 面向的正是那些希望走 Nitro 更新通道、并享受 HMR 同步能力的场景。
实战:为模块注入可覆盖的运行时配置
模块的最佳实践是:把需要暴露给运行期代码的模块选项写入 runtimeConfig,而不是在运行时直接 import 模块内部变量。Kit 文档与模块指南(Expose Options to Runtime)给出的推荐写法如下:
// 模块内(构建期)
import { defineNuxtModule } from '@nuxt/kit'
import { defu } from 'defu'
export default defineNuxtModule({
setup (options, nuxt) {
// 用 defu 扩展用户的 public runtimeConfig,而不是整体覆盖
nuxt.options.runtimeConfig.public.myModule = defu(
nuxt.options.runtimeConfig.public.myModule,
{
foo: options.foo,
},
)
},
})
随后在模块自身的 setup 或其他构建期钩子中,就可以通过 Kit 的 useRuntimeConfig() 读取被环境变量解析过的完整视图:
import { useRuntimeConfig } from '@nuxt/kit'
// 在模块构建期读取,此时已应用 NUXT_PUBLIC_MY_MODULE_FOO 等环境变量覆盖
const options = useRuntimeConfig().public.myModule
应用代码(插件、组件、服务端路由)则无需知道模块的存在,直接用标准的 useRuntimeConfig() 消费这些键即可。这样的一条链路带来的实际价值是:
- 用户可以用
NUXT_PUBLIC_MY_MODULE_FOO=xxx这样的环境变量在不修改代码的前提下覆盖模块默认值——前提是该键已在nuxt.config/模块注入的 runtimeConfig 中声明(Kit 只允许覆盖已声明键,这也是安全设计,避免任意环境变量泄漏进应用); - 模块选项与运行期解耦,客户端打包时只注入
public分支; - 由
defu保证"用户配置优先于模块默认值"的合并语义。
安全红线:凡是写入
runtimeConfig.public的键都会被序列化进客户端 payload,公开到浏览器 bundle。模块作者切勿把 API 密钥、内部 token 等敏感项放进 public 分支;应放入非 public 的私有键,仅供服务端使用。这一点同样在 recipes-basics.md 中被以 warning 形式强调。
与运行期配置机制的衔接与边界
Kit 的这两个工具与整套 Runtime Config 体系(Runtime Config 指南)是同一枚硬币的两面:
| 维度 | Kit(本文) | App 运行期 |
|---|---|---|
| 导入来源 | @nuxt/kit |
应用内自动导入的 composable |
| 执行时机 | 构建期(模块 setup 等) | 客户端 / 服务端运行期 |
| 数据源 | nuxt.options.nitro.runtimeConfig + process.env |
已序列化注入的 payload / Nitro 运行期配置 |
| 环境变量前缀 | 构建期读 NITRO_ 与 NUXT_ 双前缀 |
运行期约定 NUXT_ 前缀 |
两点务实的边界提醒:
- Kit 的
useRuntimeConfig只能在构建期调用,并且依赖 Nuxt 上下文(useNuxt)。切勿把它放进模块注入的插件或运行时代码里——那会抛出 "Nuxt instance is unavailable" 一类的上下文错误。 - Kit 的
updateRuntimeConfig修改的是构建期配置,其 HMR 联动只服务于开发态 Nitro 配置热重载;部署后配置的真正来源仍是部署环境的NUXT_环境变量。想让修改在运行期对用户生效,应通过nuxt.options.runtimeConfig在构建前完成注入,而非在运行时动态写入。
小结
useRuntimeConfig():无参调用,返回nuxt.options.nitro.runtimeConfig的深拷贝,并已按NITRO_/NUXT_双前缀应用环境变量覆盖,复刻 Nitro 运行期解析语义(含destr类型转换与可选的{{VAR}}展开);updateRuntimeConfig(config):以defu合并语义增量更新构建期配置,Nitro 已初始化时同步触发updateConfig并带来 HMR 重载,未初始化时静默降级;- 二者的实现细节、合并优先级与类型转换边界,都可以在 packages/kit/src/runtime-config.ts 与其配套测试 packages/kit/src/runtime-config.test.ts 中找到一手证据,测试中甚至包含 fast-check 属性测试(500 轮随机配置验证"仅精确覆盖目标叶子键、其他键不被污染"),可作为理解行为边界的权威参考。
在设计模块的配置面时,牢记本文的安全与职责边界:public 分支只放公开配置,私有键只服务服务端;构建期注入 + NUXT_ 环境变量覆盖 + 运行期 useRuntimeConfig() 消费,才是 Nuxt 生态中规范且可维护的配置链路。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00