Nuxt Bridge 的 Legacy Composition API 迁移指南:从 @nuxtjs/composition-api 平滑对齐 Nuxt 3
Nuxt Bridge 是运行在 Nuxt 2 项目上的"前向兼容层"模块,它让你不必一次性重写整个应用,就能逐步体验并迁移到 Nuxt 3 的 API 体系。本文聚焦 Bridge 中最关键的一环——如何从 Nuxt 2 时代的 @vue/composition-api / @nuxtjs/composition-api 迁移到与 Nuxt 3 完全对齐的 Composition API 语法,读完你将掌握依赖清理、bridge.capi 配置、各 legacy composable 的等价替换方案以及自动导入机制,为最终升级到 Nuxt 3/4 扫清最大障碍。
背景:Bridge 与 Composition API 的位置关系
在开始迁移之前,先明确 Nuxt Bridge 的定位。根据 Bridge Overview,Bridge 是一个"前向兼容层",通过安装并启用一个 Nuxt 模块,即可在 Nuxt 2 项目中体验大量 Nuxt 3 特性,从而让项目"几乎准备好"迁移到 Nuxt 3,并允许你分步骤渐进式过渡。
Bridge 文档将升级拆解为多个可独立执行的步骤,其中与本主题直接相关的两步是:
- Migrate Legacy Composition API(即本文主题,处理旧版
@nuxtjs/composition-api的存量代码) - Migrate New Composition API(在清理旧代码后,把关键函数替换为 Nuxt 3 风格的新 composable)
Nuxt Bridge 提供的 Composition API 是专门与 Nuxt 3 对齐的。这意味着:即使你此前已经在用 Composition API,启用 Bridge 时仍有少量额外步骤——因为 Bridge 的实现与旧的 @nuxtjs/composition-api 存在差异,且提供的 composable 集合以 Nuxt 3 为准。有些旧 composable 被移除且暂无替代品,因此迁移过程需要逐一处理。
第一步:移除旧依赖
无论你之前用的是哪种 Composition API 方案,首先都应从 package.json 与 nuxt.config 中移除两个旧包:
- 从依赖中移除
@vue/composition-api; - 从依赖中移除
@nuxtjs/composition-api(同时从nuxt.config的modules/buildModules中移除对应的模块声明)。
移除之后,Composition API 的支持完全交由 Nuxt Bridge 提供,不需要再手动注册任何东西(详见下文)。
仅使用过 @vue/composition-api 的场景:几乎零成本
如果你的代码只依赖 @vue/composition-api(例如手动执行过 Vue.use(VueCompositionApi)),而没有使用 @nuxtjs/composition-api,迁移会非常直接:
1. 删除手动注册 Composition API 的插件。 这个注册工作现在由 Nuxt Bridge 自动完成,你需要把形如下面的代码从插件中删掉:
- import Vue from 'vue'
- import VueCompositionApi from '@vue/composition-api'
-
- Vue.use(VueCompositionApi)
2. 除此之外无需做任何事。 可选操作是:删除代码中 @vue/composition-api 的显式 import,改为依赖 Nuxt Bridge 的自动导入(auto-import)机制——ref、computed 等 Vue API 都会被自动注入。关于自动导入的完整约定可参考 Auto-imports 概念文档。
这一"无缝兼容"的设计在当前仓库的核心实现中也能找到佐证:现代 Nuxt 在初始化时会把 vue-demi 与 @vue/composition-api 两个模块名直接别名(alias)到应用内置的 compat 目录(见 packages/nuxt/src/core/nuxt.ts 中的 options.alias['vue-demi'] 与 options.alias['@vue/composition-api'])。别名指向的 shim 文件 packages/nuxt/src/app/compat/capi.ts 会 export * from 'vue',并额外提供 install、set、del 等空实现/兼容函数;packages/nuxt/src/app/compat/vue-demi.ts 则直接声明 isVue3 = true。也就是说,只要代码中保留了 import { ref } from '@vue/composition-api' 这样的写法,shim 也能保证其继续可用——这为从 Bridge 平滑过渡到 Nuxt 3/4 提供了一致的底层保障。
从 @nuxtjs/composition-api 迁移:差异与例外
与直接使用 @vue/composition-api 不同,@nuxtjs/composition-api 的用户会面临更多改动,因为:
- Nuxt Bridge 对 Composition API 的实现与
@nuxtjs/composition-api略有不同; - Bridge 提供的 composable 以 Nuxt 3 的 composable 集合为基准,部分旧 composable 被移除且暂未提供替代品。
从 buildModules 移除 @nuxtjs/composition-api/module
你不必立即修改全部 import——在移除模块之后,Nuxt Bridge 会自动为目前代码中的大多数 import 提供 "shim"(垫片),从而给你留出迁移到新 composable 的时间。但有以下几个例外,它们已经被彻底移除,必须手动处理:
withContext已移除。 需要迁移到useNuxtApp/useState等新 API,参见 Migrate New Composition API 中关于useContext和withContext的说明。useStatic已移除。 目前没有替代品;如果你确实需要它,可以在社区发起 discussion 说明你的使用场景。reqRef与reqSsrRef(此前已被标记为 deprecated)已彻底移除。 请按照下文中ssrRef/shallowSsrRef的替换指引(见 Migrate New Composition API 的对应小节)进行迁移。
设置 bridge.capi
为了让 Bridge 提供 Nuxt 3 风格的 Composition API(并兼容上述 legacy shim),需要在 nuxt.config 中显式启用 capi 特性(该特性默认开启,此处为显式声明):
import { defineNuxtConfig } from '@nuxt/bridge'
export default defineNuxtConfig({
bridge: {
capi: true,
nitro: false, // 如果已完成向 Nitro 的迁移,请改为 true
},
})
结合 Bridge Configuration 中的特性开关说明,capi 还支持更细粒度的控制:
// Disable Composition API support entirely
// capi: false,
// ... or just disable legacy Composition API support
// capi: {
// legacy: false
// },
也就是说,bridge.capi: false 会整体关闭 Composition API 支持;而 bridge.capi: { legacy: false } 只关闭旧版(@nuxtjs/composition-api 风格)的兼容支持,Nuxt 3 风格的新 API 仍然可用。此外 nitro: false 意味着暂用 legacy server;若已完成 Nitro 迁移则设为 true。其余在用的每个 @nuxtjs/composition-api composable,按下面的步骤逐一替换。
逐个 composable 的迁移对照
useFetch:$fetchState 与 $fetch 被移除
Bridge 版本不再返回 $fetch 与 $fetchState,需要改用 fetch 与 fetchState:
const {
- $fetch,
- $fetchState,
+ fetch,
+ fetchState,
} = useFetch(() => { posts.value = await $fetch('/api/posts') })
注意其中的 useFetch(() => ...) 内部回调依然是旧式写法(在回调内通过副作用给 posts 赋值),这仅用于展示属性名变化;后续若迁移到 Nuxt 3 风格的 useLazyFetch / useLazyAsyncData,应改为由它们返回响应式 data,详见 Migrate New Composition API 的 useAsync / useFetch 小节,以及 Data Fetching 中对 useAsyncData / useFetch 的完整讲解。
defineNuxtMiddleware:类型辅助桩函数已移除
defineNuxtMiddleware 只是一个类型辅助的桩函数,现已移除。直接去掉这一层包装:
- import { defineNuxtMiddleware } from '@nuxtjs/composition-api`
- export default defineNuxtMiddleware((ctx) => {})
+ export default (ctx) => {}
如果需要 TypeScript 类型支持,可以改用 @nuxt/types 提供的 Middleware 类型:
import type { Middleware } from '@nuxt/types'
export default <Middleware> function (ctx) { }
defineNuxtPlugin:类型辅助桩函数已移除
与 defineNuxtMiddleware 同理,defineNuxtPlugin 也是一个被移除的类型辅助桩函数。如果你希望继续使用 Nuxt 2 风格的插件,去掉函数包装即可:
- import { defineNuxtPlugin } from '@nuxtjs/composition-api'
- export default defineNuxtPlugin((ctx, inject) => {})
+ export default (ctx, inject) => {}
需要 TypeScript 支持时,可用 @nuxt/types 的 Plugin 类型:
import type { Plugin } from '@nuxt/types'
export default <Plugin> function (ctx, inject) {}
::warning
上例虽然有效,但请注意:Nuxt 3 引入了签名略有不同的新 defineNuxtPlugin 函数。若希望一步到位迁移到 Nuxt 3 插件格式(插件只接收单个 nuxtApp 参数),请参考 Creating Plugins 文档 以及 Plugins and Middleware 迁移文档 中的新插件写法(示例中使用 nuxtApp.provide('injected', ...) 提供可在模板/实例中以 $injected 访问的能力)。
::
useRouter 与 useRoute:直接替换,但 route 不再是 computed
Bridge 为这两个 composable 提供了直接替代品:useRouter 与 useRoute(分别对应 useRouter 与 useRoute)。
二者之间唯一关键差异是:Bridge(及 Nuxt 3)的 useRoute() 不再返回 computed 属性,因此访问路径不再需要 .value:
- import { useRouter, useRoute } from '@nuxtjs/composition-api'
const router = useRouter()
const route = useRoute()
- console.log(route.value.path)
+ console.log(route.path)
也就是说,如果旧代码中存在大量 route.value.xxx 的访问,需要全局移除 .value。useRoute() 返回的 route 对象本身已具备响应式属性(例如动态参数、查询参数等都会随导航更新),详情可阅读 useRoute API 文档。
向 Nuxt 3 风格 API 迈进:ssrRef、useContext 等的归宿
原文档中明确指出 withContext、useStatic、reqRef / reqSsrRef 的替代品需要参考 Migrate New Composition API。这里给出其中几项的关键迁移方向,帮助你一次性规划到位:
ssrRef/shallowSsrRef→useState。 新 composable 在底层工作方式上非常接近,但你必须显式提供key(旧实现会自动生成),而且useState只能在组件实例或 Nuxt 3 插件(defineNuxtPlugin)上下文中调用,不能在全局/环境上下文中使用,以避免跨请求共享状态:
- import { ssrRef } from '@nuxtjs/composition-api'
- const ref1 = ssrRef('initialData')
+ const ref1 = useState('ref1-key', () => 'initialData')
// 访问方式保持不变
console.log(ref1.value)
keyed 状态意味着只要使用相同 key,就能在多个位置访问同一份状态。详见 useState API 文档。
useContext/useStore→useNuxtApp。 通过useNuxtApp()可以访问注入的 helper(如$axios)与 Vuex store:
- import { useContext } from '@nuxtjs/composition-api'
+ const { $axios } = useNuxtApp()
- import { useStore } from '@nuxtjs/composition-api'
+ const { $store } = useNuxtApp()
另需注意:useNuxtApp() 还暴露一个 nuxt2Context key,包含 Nuxt 2 context 的全部属性,但官方不建议直接使用它(它在 Nuxt 3 中不存在),应尽量寻找其他访问途径。详见 useNuxtApp API 文档。
-
onGlobalSetup→ 插件 + hook。 可在defineNuxtPlugin内通过nuxtApp.hook('vue:setup', ...)实现等价逻辑,也可以在 layout 的setup()中运行自定义代码。 -
useAsync/useFetch→useLazyAsyncData/useLazyFetch。 这些"lazy"版本与旧 composable 一样不会在客户端阻塞路由导航,但 API 完全不同(不要试图在 composable 外部修改其他变量)。且useLazyFetch需要先启用 Nitro。 -
useMeta→useNuxt2Meta或useHead。 在 Bridge 中可继续用useNuxt2Meta(以vue-meta兼容方式操作 meta,支持响应式更新,但 Nuxt 3 不支持);也可以显式启用bridge.meta: true后使用 Nuxt 3 兼容的 useHead(底层基于@unhead/vue)。注意不要在同一组件内混用useNuxt2Meta()与 Options API 的head(),也不要混用原生 Nuxt 2 的head()属性与useHead。
关于自动导入:#imports 与显式禁用
完成上述替换后,若代码中仍保留旧包的显式 import(例如 import { ref } from '@nuxtjs/composition-api'),建议逐步移除,转而依赖 Bridge 的自动导入。与 Nuxt 3/4 一致,Nuxt 也通过 #imports 别名暴露所有自动导入项,供需要时显式引用:
<script setup lang="ts">
import { computed, ref } from '#imports'
const count = ref(1)
const double = computed(() => count.value * 2)
</script>
如果你想彻底关闭 composable 与工具函数的自动导入,可在 nuxt.config 中设置 imports.autoImport: false(此时仍可从 #imports 显式导入):
export default defineNuxtConfig({
imports: {
autoImport: false,
},
})
更完整的自动导入行为说明(包含 imports.scan 等进阶选项)可参考 Auto-imports 概念文档。
迁移后的验证与后续升级路径
完成上述改造后,建议按 Bridge Overview 的指引回归验证:确保 dev server 未运行,将 package.json 中的脚本由 nuxt 切换为 nuxt2(例如 dev: "nuxt2"、build: "nuxt2 build"、start: "nuxt2 start"),先运行一次确认应用行为与之前一致,再继续下一步。
Nuxt Bridge 的整套迁移不需要一次性完成,你可以按需逐项推进。与本主题衔接紧密的后续步骤包括:
- TypeScript 迁移——先补齐类型基础;
- Plugins and Middleware 迁移——迁移到
defineNuxtPlugin/defineNuxtRouteMiddleware新格式,并通过macros.pageMeta: true启用definePageMeta(仅限middleware与layout); - Migrate New Composition API——完成
ssrRef→useState、useMeta→useNuxt2Meta/useHead等最终替换; - 之后依次处理 Meta Tags、Runtime Config、Nitro、Vite 等选项。
需要提醒的是:Bridge 本质上仍运行在 Nuxt 2 之上,它追求与 Nuxt 3 的特性对齐,但也存在若干已知边界(例如 Bridge 文档在 overview 中提示 useAsyncData / useFetch 等 composable 存在使用限制,请以 Bridge Overview 与 Bridge Configuration 中列出的特性开关为准)。因此在动手改造存量代码前,先对照特性开关梳理项目中实际用到的 composable 清单,能显著降低迁移过程中的返工成本。
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 StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00