首页
/ Nuxt Bridge 迁移指南:从 `@nuxtjs/composition-api` 升级到 Nuxt 3 兼容的 Composition API

Nuxt Bridge 迁移指南:从 `@nuxtjs/composition-api` 升级到 Nuxt 3 兼容的 Composition API

2026-09-07 16:03:22作者:韦蓉瑛

本篇技术指南面向正运行 Nuxt 2 + @nuxtjs/composition-api、并计划通过 Nuxt Bridge 平滑过渡到 Nuxt 3 的项目。文章以 @nuxtjs/composition-api 的旧式组合式函数为对照基线,逐一讲解 ssrRefuseFetchuseStoreuseContextwrapPropertyuseMeta 等旧 API 应如何迁移到 useStateuseLazyAsyncDatauseLazyFetchuseNuxtAppuseHead 等新 API,并延伸讲解显式导入 #imports 与关闭自动导入的配置方法。读完本文,你将获得一份可直接对照修改的迁移路线图与逐行 diff 示例。

本文对应仓库文档为 docs/6.bridge/5.nuxt3-compatible-api.md。在 Nuxt Bridge 的升级路径(参见 6.bridge/1.overview.md)中,"Migrate New Composition API" 是关键一环:从 @nuxtjs/composition-api 迁移到 Nuxt 3 兼容的 API 后,将来真正升级 Nuxt 3 时几乎无需再改写代码。除迁移对照外,正文会结合当前 Nuxt 仓库的源码实现(如 state.ts)说明新 API 的底层工作方式,帮助你理解“为什么要这样改”。

迁移概览:一张 API 对照表

以下速查表总结了 @nuxtjs/composition-api 常用导出与 Nuxt 3 兼容 API 的对应关系,后文将对每一项给出详细说明与迁移示例。

@nuxtjs/composition-api Nuxt 3 / Nuxt Bridge 兼容方案 状态
ssrRef / shallowSsrRef useState(key, init) 以新组合式函数替代,需显式提供 key
ssrPromise 无替代实现 已移除,需自行寻找替代方案
onGlobalSetup defineNuxtPlugin + nuxtApp.hook('vue:setup', ...) 已移除,改用其他途径
useStore useNuxtApp().$store 需改写
useContext / withContext useNuxtApp() 解构 需改写
wrapProperty 自实现(见下文代码) 不再内置提供
useAsync useLazyAsyncData(key, handler) API 完全不同,需重写
useFetch useLazyFetch(url) API 完全不同,需重写
useMeta useNuxt2Meta()(仅 Bridge)/ useHead()(需开启 bridge.meta 二选一

ssrRefshallowSsrRef:统一收敛为 useState

这两个旧函数在 Nuxt 3 兼容层中被一个工作方式非常相似的新组合式函数取代:useState

关键差异有两点:

  1. 必须为状态提供一个 key。旧 API 中 key 由 Nuxt 自动生成,而 useState 要求显式传入。
  2. 调用环境受限useState 只能在 Nuxt 3 插件(由 defineNuxtPlugin 定义)或组件实例内调用,不能用于全局/环境上下文(ambient context),原因在于全局共享状态存在跨请求串扰(shared state across requests)的危险——多个用户请求共用同一份状态将导致数据泄漏。

迁移示例(diff):

- import { ssrRef } from '@nuxtjs/composition-api'

- const ref1 = ssrRef('initialData')
- const ref2 = ssrRef(() => 'factory function')
+ const ref1 = useState('ref1-key', () => 'initialData')
+ const ref2 = useState('ref2-key', () => 'factory function')
  // accessing the state
  console.log(ref1.value)

注意 init 既可以返回普通值,也可以返回一个工厂函数;状态读取方式不变,仍是 xxx.value

由于状态以 key 为标识,只要使用相同的 key,你就能在任意多个位置访问同一份状态——这正是旧 API 自动生成的随机 key 无法提供的特性。详细用法见仓库中的 useState API 文档

源码视角:useState 的 key 与 payload

在当前仓库的 packages/nuxt/src/app/composables/state.ts 中可以看到 useState 的实现骨架:

  • 所有状态都以 $s 为前缀挂载在 nuxtApp.payload.state 下(const state = toRef(nuxtApp.payload.state, key)),因此状态天然可以跨端(服务端渲染出的状态会被序列化到 payload,供客户端 hydration 时恢复);
  • 首次访问且值仍为 undefined 时才会调用 init 工厂函数生成初始值;
  • init 若返回一个 ref,Nuxt 会直接把该 ref 存入 payload,实现“浅响应式”的按需优化——这也是文档建议在状态包含大对象/大数组时配合 shallowRef 提升性能的原因(见 useState 文档中的 shallowRef 小节)。

另外,useState 中的状态最终会经过 JSON 序列化写入 payload,因此不要在其中存放无法序列化的内容(如 class 实例、函数、symbol)。

ssrPromise:已移除

ssrPromise 函数在新 API 中已被移除,如果你此前在使用它,需要自行寻找替代实现。若你确实存在必须使用 ssrPromise 的业务场景,建议通过 Nuxt 官方讨论区提出,以便官方评估是否在后续版本中提供等价能力。迁移时不要试图寻找同名函数,而是用 useStateuseAsyncData/useLazyAsyncData 等手段重新组织“仅服务端执行一次”的逻辑。

onGlobalSetup:改由插件 + vue:setup 钩子实现

onGlobalSetup 已被移除,但其使用场景可以通过在 defineNuxtPlugin 内调用 useNuxtAppuseState 来满足;你同样可以把任意自定义代码放进某个布局(layout)的 setup() 函数中执行。

迁移示例(diff):

- import { onGlobalSetup } from '@nuxtjs/composition-api'

- export default () => {
-   onGlobalSetup(() => {
+ export default defineNuxtPlugin((nuxtApp) => {
+   nuxtApp.hook('vue:setup', () => {
      // ...
    })
- }
+ })

源码视角:vue:setup 运行时钩子

nuxtApp.hook('vue:setup') 在全局 setup 阶段执行”的说法在当前仓库中可以得到印证:在 packages/nuxt/src/app/nuxt.ts 的运行时钩子类型中定义了 'vue:setup': () => void;而根组件 packages/nuxt/src/app/components/nuxt-root.vue 中通过 nuxtApp.hooks.callHookWith(..., 'vue:setup', []) 真正触发该钩子。同时注意钩子回调必须是同步的(诊断项 NUXT_E1011,见 packages/nuxt/src/app/diagnostics/core.ts),异步工作应移入 app:created 等其他钩子。

useStore:通过 useNuxtApp().$store 访问

为了访问 Vuex store 实例,只需解构 useNuxtApp() 返回的上下文中的 $store 即可:

- import { useStore } from '@nuxtjs/composition-api'
+ const { $store } = useNuxtApp()

useContextwithContext:使用 useNuxtApp

旧 API 通过 useContext() 取到的注入辅助函数(例如 $axios 之类在 Nuxt 2 插件中通过 app.$axios 暴露的实例),现在一律改为从 useNuxtApp() 中解构:

- import { useContext } from '@nuxtjs/composition-api'
+ const { $axios } = useNuxtApp()

::note useNuxtApp() 额外暴露了一个名为 nuxt2Context 的 key,其中包含从 Nuxt 2 context 中能取到的全部属性。但不建议直接使用它——nuxt2Context 在 Nuxt 3 中并不存在,依赖它会让代码在真正升级 Nuxt 3 时再次返工。请优先寻找其他访问途径;若实在没有,可在 Nuxt 官方提交 feature request 或发起讨论。 ::

useNuxtApp 的完整能力(providehookcallHookpayloadrunWithContext 等)可参考仓库中的 useNuxtApp API 文档;其实现位于 packages/nuxt/src/app/nuxt.ts,当运行环境中没有 Nuxt 上下文时会抛出异常(对应诊断码 NUXT_E1001)。

wrapProperty:不再提供,给出等价自实现

wrapProperty 辅助函数已不再提供,但可以非常简单地用下面的代码等价替换:

import { computed, getCurrentInstance } from 'vue'

const wrapProperty = (property: string, makeComputed = true) => () => {
  const vm = getCurrentInstance().proxy
  return makeComputed ? computed(() => vm[property]) : vm[property]
}

它的行为与旧版一致:包装一个实例属性名,默认返回计算属性(makeComputed = true 时惰性求值并保持响应性),传 false 则直接返回当前值。

useAsyncuseFetch:改用 useLazyAsyncData / useLazyFetch

这两个旧组合式函数可以分别替换为 useLazyAsyncDatauseLazyFetch。替换后依然保持“在客户端不阻塞路由导航”的行为——这正是名称中 lazy 的含义(数据在后台加载,页面先完成导航渲染)。

::important 尽管新旧名称听上去很像,API 签名是完全不同的。尤其注意:不要像旧 useFetch 那样在组合式函数内部对外部变量赋值(副作用式写法),新 API 一律通过返回值来驱动状态。 ::

::warning 在 Nuxt Bridge 中使用 useLazyFetch 前,必须先完成 Nitro 的配置与启用——否则请求层尚未接入,useLazyFetch 无法工作。另外,Bridge 场景下 useAsyncData / useFetch(非 lazy 版本)并不可用,这也正是本文推荐 lazy 版本的原因(参见 6.bridge/1.overview.md 中的警告)。 ::

useAsync 迁移

<script setup>
- import { useAsync } from '@nuxtjs/composition-api'
- const posts = useAsync(() => $fetch('/api/posts'))
+ const { data: posts } = useLazyAsyncData('posts', () => $fetch('/api/posts'))
+ // or, more simply!
+ const { data: posts } = useLazyFetch('/api/posts')
</script>

useFetch 迁移

<script setup>
- import { useFetch } from '@nuxtjs/composition-api'
- const posts = ref([])
- const { fetch } = useFetch(() => { posts.value = await $fetch('/api/posts') })
+ const { data: posts, refresh } = useLazyAsyncData('posts', () => $fetch('/api/posts'))
+ // or, more simply!
+ const { data: posts, refresh } = useLazyFetch('/api/posts')
  function updatePosts() {
-   return fetch()
+   return refresh()
  }
</script>

两处迁移的要点相同:旧写法中的“手动重新拉取”函数 fetch() 对应新 API 的 refresh(),数据赋值改为对 data 的解构与读取,不再手工写入外部 ref。数据获取的完整概念与最佳实践可参考 getting-started 中的数据获取指南

源码视角:lazy 变体是如何实现的

在 Nuxt 3 中,lazy 只是普通版本的一个工厂参数。看当前仓库的实现即可一目了然:

因此,迁移时你并不需要重新学习两套 API——lazy 版本返回的数据结构与普通版完全一致(datarefresherrorstatuspendingclear 等字段,详见 useLazyFetch API 文档),区别只在导航是否等待请求完成。在模板中建议根据 status 区分 pending / error / 成功三种状态渲染,可参考 useLazyFetch 文档中的加载态示例

Meta 处理:useNuxt2MetauseHead

useNuxt2Meta 保持 vue-meta 兼容

如需继续以 vue-meta 兼容的方式操作 meta 标签,可以使用 useNuxt2Meta。它在 Nuxt Bridge 中可用(在 Nuxt 3 中不可用):

<script setup>
- import { useMeta } from '@nuxtjs/composition-api'
  useNuxt2Meta({
    title: 'My Nuxt App',
  })
</script>

useNuxt2Meta 同样支持传入计算属性或 ref,meta 值会随之响应式更新:

<script setup>
const title = ref('my title')
useNuxt2Meta({
  title,
})
title.value = 'new title'
</script>

::note 在同一个组件中,不要同时使用 useNuxt2Meta() 与 Options API 的 head(),二者并存时行为不可预测。 ::

useHead 接入 Nuxt 3 风格的 meta

Nuxt Bridge 还提供了一套 Nuxt 3 兼容的 meta 实现,通过 useHead 组合式函数访问。它底层使用 @unhead/vue(而非 vue-meta)来操作 <head>

<script setup>
- import { useMeta } from '@nuxtjs/composition-api'
  useHead({
    title: 'My Nuxt App',
  })
</script>

启用它需要在 nuxt.config 中显式开启 bridge.meta(这是一项 opt-in 特性,默认关闭):

import { defineNuxtConfig } from '@nuxt/bridge'

export default defineNuxtConfig({
  bridge: {
    meta: true,
  },
})

配置项含义可对照 6.bridge/10.configuration.md 中的 Feature Flags 说明。同样,由于 useHead 与原生 Nuxt 2 的 head() 属性机制不同(一个走 @unhead/vue、一个走 vue-meta),不建议在同一项目里混用两者,以免相互冲突

useHead 的更多用法(titlemetalinkscript 以及各种响应式取值)详见 useHead API 文档SEO 与 Meta 指南

显式导入:通过 #imports 别名

Bridge/Nuxt 3 会为每个可自动导入的 API 暴露一个 #imports 别名,当你需要把自动导入改为显式导入时可以直接使用:

<script setup lang="ts">
import { computed, ref } from '#imports'

const count = ref(1)
const double = computed(() => count.value * 2)
</script>

这不仅便于阅读代码时明确依赖来源,也是在配合“关闭自动导入”或类型检查工具时保持代码可运行的重要手段。

关闭自动导入

若希望彻底关闭组合式函数与工具函数的自动导入,可在 nuxt.config 中设置 imports.autoImport: false

export default defineNuxtConfig({
  imports: {
    autoImport: false,
  },
})

注意,这会完全关闭自动导入,但并不会影响显式导入:你依然可以通过上面的 #imports 别名按需导入所需 API。自动导入机制的整体设计(内置自动导入清单、目录扫描、显式导入等)见 guide/concepts 的自动导入概念文档,其中同样记录了 #imports 显式导入与关闭自动导入的等价配置写法。

小结:一次改写,为 Nuxt 3 铺路

Nuxt Bridge 的定位是一个“前向兼容层”,让你在 Nuxt 2 项目上提前体验并采用 Nuxt 3 的 API(详见 6.bridge/1.overview.md)。按本文顺序逐项替换后,你的代码中来自 @nuxtjs/composition-api 的依赖将被清零:SSR 共享状态交给 useState、全局逻辑迁入插件与 vue:setup 钩子、上下文访问统一收敛到 useNuxtApp、数据获取切换为 useLazyAsyncData / useLazyFetch、meta 处理在 useNuxt2MetauseHead 之间按需二选一,最后通过 #imports 显式导入与 imports.autoImport 掌控自动导入粒度。

这样做最大的收益正如本文开头所述:将来从 Bridge 正式迁移到 Nuxt 3 时,几乎不再需要第二次改写。迁移过程不要求一步到位,可以按上述小节逐个渐进完成,每个步骤都可独立验证后再进入下一步。

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

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.76 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
860
1.35 K
docsdocs
暂无描述
Markdown
899
5.83 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
925
1.85 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.84 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
533
601
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.03 K
525
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.37 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
395