首页
/ Nuxt Kit 运行时配置 API 深度指南:useRuntimeConfig 与 updateRuntimeConfig 在模块开发中的实战应用

Nuxt Kit 运行时配置 API 深度指南:useRuntimeConfig 与 updateRuntimeConfig 在模块开发中的实战应用

2026-09-07 10:07:45作者:秋泉律Samson

本篇文章面向 Nuxt 模块作者与进阶开发者,聚焦 Nuxt Kit 暴露的两个**构建期(build-time)**运行时配置工具——useRuntimeConfigupdateRuntimeConfig。文章将结合 @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/kitpackages/kit/src/runtime-config.ts 导出,其对外出口位于 packages/kit/src/index.tsexport { 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)可以看到四个关键事实:

  1. 数据来源是 Nitro 的构建期配置:通过 useNuxt() 拿到 Nuxt 实例后,读取 nuxt.options.nitro.runtimeConfig。也就是说,任何模块、插件在此之前通过 nuxt.options.nitro.runtimeConfig 写入的内容都会被纳入返回结果。
  2. 返回的是深拷贝:结果先经 klona 深克隆,避免你意外修改到 Nuxt 实例上的原始配置对象。
  3. 环境变量覆盖规则:会以 NITRO_ 为前缀、NUXT_ 为备选前缀,逐层查找并覆盖配置项——与 Nitro 运行期行为一致。例如配置键 apiSecret 会被 NITRO_API_SECRETNUXT_API_SECRET 覆盖,嵌套键则用 _ 连接(如 public.apiBase 对应 NUXT_PUBLIC_API_BASE)。
  4. 环境变量展开可选:是否支持字符串内嵌 {{VAR}} 展开取决于 nitro.experimental.envExpansion,默认回退到读取进程环境变量 NITRO_ENV_EXPANSION 是否为真。

applyEnv:逐层覆盖的内部语义

环境变量的覆盖并非简单的"整对象替换",其内部递归逻辑(packages/kit/src/runtime-config.ts)值得模块作者注意,因为它决定了用户能以多细的粒度覆盖配置:

  • 每个配置键先经 sculesnakeCase 转为 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,其行为可拆解为三层:

  1. defu 默认合并,而非覆盖:传入的 runtimeConfig 会以"新值优先"的方式合并进已存在的 nuxt.options.nitro.runtimeConfigdefu 不会用 undefined 覆盖已有值),然后通过 Object.assign 写回 Nuxt 实例上的 Nitro 配置。这意味着你可以只传模块关心的键,其余键保持用户配置不变。
  2. 联动 Nitro 配置热更新:若 Nitro 实例已经可用,会调用 useNitro().updateConfig({ runtimeConfig }) 把变更同步给 Nitro。原文档特别指出——当 Nitro 已初始化时,这会触发一次 HMR 事件来重载 Nitro 的运行时配置,使开发模式下模块对配置的修改能即时生效。
  3. 对"尚未初始化"的容错:如果 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_ 前缀

两点务实的边界提醒:

  1. Kit 的 useRuntimeConfig 只能在构建期调用,并且依赖 Nuxt 上下文(useNuxt)。切勿把它放进模块注入的插件或运行时代码里——那会抛出 "Nuxt instance is unavailable" 一类的上下文错误。
  2. 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 生态中规范且可维护的配置链路。

登录后查看全文
热门项目推荐
相关项目推荐