2025 年以「三」的力量开启!
新年伊始,我们喜爱的工具迎来了重大更新。UI 团队即将发布 UI / UI Pro 库 的 3 版本(目前处于 alpha 版本),而 Content 团队已经发布了 Nuxt Content v3。
这些更新意味着所有结合了 Content 和 UI 的入门模板都需要更新以适配最新版本。为帮助您顺利完成迁移,本指南将演示如何将 Nuxt UI Pro Docs Starter 迁移到全新的 Content v3 和 Nuxt UI v3 包。
pnpm add @nuxt/content@^3
yarn add @nuxt/content@^3
npm install @nuxt/content@^3
bun add @nuxt/content@^3
content.config.ts 文件该配置文件定义数据结构。集合(collection)代表一组相关内容。以 docs starter 为例,包含两个不同的集合,landing 集合代表主页,另一个 docs 集合包含文档页面。
import { defineContentConfig, defineCollection, z } from '@nuxt/content'
export default defineContentConfig({
collections: {
landing: defineCollection({
type: 'page',
source: 'index.yml'
}),
docs: defineCollection({
type: 'page',
source: {
include: '**',
exclude: ['index.yml']
},
schema: z.object({
links: z.array(z.object({
label: z.string(),
icon: z.string(),
to: z.string(),
target: z.string().optional()
})).optional()
})
})
}
})
在 page 类型提供的内置字段基础上,我们为 docs 集合额外添加了 links 字段,以便在文档的 页面头部 有选择地显示它们。
type: page 表示内容文件与网站页面之间存在一一对应关系。app.vuefetchContentNavigation 迁移为 queryCollectionNavigationconst { data: navigation } = await useAsyncData('navigation', () => queryCollectionNavigation('docs'))
const { data: navigation } = await useAsyncData('navigation', () => fetchContentNavigation())
queryCollectionSearchSectionsconst { data: files } = useLazyAsyncData('search', () => queryCollectionSearchSections('docs'), {
server: false,
})
const { data: files } = useLazyFetch<ParsedContent[]>('/api/search.json', {
default: () => [],
server: false
})
queryContent 迁移为 queryCollectionconst { data: page } = await useAsyncData('index', () => queryCollection('landing').path('/').first())
const { data: page } = await useAsyncData('index', () => queryContent('/').findOne())
page 类型提供的 seo 字段填充 useSeoMetauseSeoMeta({
title: page.value.seo.title,
ogTitle: page.value.seo.title,
description: page.value.seo.description,
ogDescription: page.value.seo.description
})
seo 字段会被根级的 title 和 description 自动覆盖。queryCollection 和 queryCollectionItemSurroundingsconst { data } = await useAsyncData(route.path, () => Promise.all([
queryCollection('docs').path(route.path).first(),
queryCollectionItemSurroundings('docs', route.path, {
fields: ['title', 'description'],
}),
]), {
transform: ([page, surround]) => ({ page, surround }),
})
const page = computed(() => data.value?.page)
const surround = computed(() => data.value?.surround)
const { data: page } = await useAsyncData(route.path, () => queryContent(route.path).findOne())
const { data: surround } = await useAsyncData(`${route.path}-surround`, () => queryContent()
.where({ _extension: 'md', navigation: { $ne: false } })
.only(['title', 'description', '_path'])
.findSurround(withoutTrailingSlash(route.path))
)
page 类型提供的 seo 字段填充 useSeoMetauseSeoMeta({
title: page.value.seo.title,
ogTitle: `${page.value.seo.title} - ${seo?.siteName}`,
description: page.value.seo.description,
ogDescription: page.value.seo.description
})
seo 字段会被根级的 title 和 description 自动覆盖。Content v3 大幅改进了类型支持,大部分手动声明类型的需求已无,类型将由 Nuxt Content API 直接提供。
文档入门模板中唯一需要关注的是导航项类型,将 NavItem 替换为 ContentNavigationItem 。
import type { ContentNavigationItem } from '@nuxt/content'
const navigation = inject<Ref<ContentNavigationItem[]>>('navigation')
所有 _dir.yml 文件命名改为 .navigation.yml
由于 studio 模块 已废弃,且新的通用 Preview API 已直接集成到 Nuxt Content,我们可以从依赖和 nuxt.config.ts 的模块列表中移除 @nuxthq/studio 包。
只需在 Nuxt 配置文件中启用预览模式,将 Studio API 绑定即可。
export default defineNuxtConfig({
content: {
preview: {
api: 'https://api.nuxt.studio'
}
},
})
最后,为了保持 app config 文件可由 Studio 更新,只需将 nuxt.schema.ts 文件中的辅助导入由 @nuxthq/studio/theme 更改为 @nuxt/content/preview。
pnpm add @nuxt/ui-pro@next
yarn add @nuxt/ui-pro@next
npm install @nuxt/ui-pro@next
bun add @nuxt/ui-pro@next
无需再在模块中添加 @nuxt/ui,因为它已被 @nuxt/ui-pro 自动引入。
export default defineNuxtConfig({
modules: ['@nuxt/ui-pro']
})
export default defineNuxtConfig({
extends: ['@nuxt/ui-pro'],
modules: ['@nuxt/ui']
})
@import "tailwindcss" theme(static);
@import "@nuxt/ui-pro";
export default defineNuxtConfig({
modules: ['@nuxt/ui-pro'],
css: ['~/assets/css/main.css']
})
Nuxt UI v3 使用 Tailwind CSS v4,采用 CSS 优先的配置方式。现可在 @theme 指令内使用 CSS 变量定制主题。
tailwind.config.ts 文件main.css 文件中使用 @theme 指令应用主题@source 指令让 Tailwind 能检测 markdown 文件中的类名@import "tailwindcss" theme(static);
@import "@nuxt/ui-pro";
@source "../content/**/*";
@theme {
--font-sans: 'DM Sans', sans-serif;
--color-green-50: #EFFDF5;
--color-green-100: #D9FBE8;
--color-green-200: #B3F5D1;
--color-green-300: #75EDAE;
--color-green-400: #00DC82;
--color-green-500: #00C16A;
--color-green-600: #00A155;
--color-green-700: #007F45;
--color-green-800: #016538;
--color-green-900: #0A5331;
--color-green-950: #052E16;
}
app.config.ts 中的 ui 配置export default defineAppConfig({
ui: {
colors: {
primary: 'green',
neutral: 'slate'
}
},
uiPro: {
footer: {
slots: {
root: 'border-t border-gray-200 dark:border-gray-800',
left: 'text-sm text-gray-500 dark:text-gray-400'
}
}
},
}
export default defineAppConfig({
ui: {
primary: 'green',
gray: 'slate',
footer: {
bottom: {
left: 'text-sm text-gray-500 dark:text-gray-400',
wrapper: 'border-t border-gray-200 dark:border-gray-800'
}
}
},
})
error.vue 页面可以使用新的 UError 组件作为完整的页面结构。
<template>
<div>
<AppHeader />
<UError :error="error" />
<AppFooter />
<ClientOnly>
<LazyUContentSearch
:files="files"
:navigation="navigation"
/>
</ClientOnly>
</div>
</template>
<template>
<div>
<AppHeader />
<UMain>
<UContainer>
<UPage>
<UPageError :error="error" />
</UPage>
</UContainer>
</UMain>
<AppFooter />
<ClientOnly>
<LazyUContentSearch
:files="files"
:navigation="navigation"
/>
</ClientOnly>
<UNotifications />
</div>
</template>
app.vue 页面Main、Footer 和 LazyUContentSearch 组件在本例中无需更新。Notification 组件可以移除,因为 Toast 由 App 组件直接管理。NavigationTree 组件可替换为 NavigationMenu 或 ContentNavigation 组件展示内容导航。<script>
// 内容导航由 queryCollectionNavigation('docs') 提供
const navigation = inject<Ref<ContentNavigationItem[]>>('navigation')
</script>
<template>
<UHeader>
<template #content>
<UContentNavigation
highlight
:navigation="navigation"
/>
</template>
</UHeader>
</template>
<script>
// 内容导航由 fetchContentNavigation() 提供
const navigation = inject<Ref<NavItem[]>>('navigation')
</script>
<template>
<UHeader>
<template #panel>
<UNavigationTree :links="mapContentNavigation(navigation)" />
</template>
</UHeader>
</template>
我们决定将首页内容从 YML 迁移到 Markdown 。
components/content 文件夹中),Content v3 会自动处理这些。export default defineContentConfig({
collections: {
landing: defineCollection({
type: 'page',
source: 'index.md'
}),
docs: defineCollection({
type: 'page',
source: {
include: '**',
exclude: ['index.md']
},
...
})
}
})
ContentRenderer 渲染 MarkdownContentRenderer 组件的 prose 属性设置为 false,以避免 Markdown 应用 prose 样式。<template>
<UContainer>
<ContentRenderer
v-if="page"
:value="page"
:prose="false"
/>
</UContainer>
</template>
<template>
<div>
<ULandingHero
v-if="page.hero"
v-bind="page.hero"
>
<template #headline>
<UBadge
v-if="page.hero.headline"
variant="subtle"
size="lg"
class="relative rounded-full font-semibold"
>
<NuxtLink
:to="page.hero.headline.to"
target="_blank"
class="focus:outline-none"
tabindex="-1"
>
<span
class="absolute inset-0"
aria-hidden="true"
/>
</NuxtLink>
{{ page.hero.headline.label }}
<UIcon
v-if="page.hero.headline.icon"
:name="page.hero.headline.icon"
class="ml-1 w-4 h-4 pointer-events-none"
/>
</UBadge>
</template>
<template #title>
<MDC cache-key="head-title" :value="page.hero.title" />
</template>
<MDC
:value="page.hero.code"
cache-key="head-code"
class="prose prose-primary dark:prose-invert mx-auto"
/>
</ULandingHero>
<ULandingSection
:title="page.features.title"
:links="page.features.links"
>
<UPageGrid>
<ULandingCard
v-for="(item, index) of page.features.items"
:key="index"
v-bind="item"
/>
</UPageGrid>
</ULandingSection>
</div>
</template>
请将 index.md 中的所有组件迁移至遵循 MDC 语法 格式。
首页组件已重组并标准化为通用的 Page 组件。
LandingHero 改为 PageHeroLandingSection 改为 PageSectionLandingCard 改为 PageCard(但我们将使用 PageFeature)Aside 组件已重命名为 PageAside。ContentNavigation 组件(替代旧的 NavigationTree)展示由 queryCollectionNavigation 返回的内容导航。<template>
<UContainer>
<UPage>
<template #left>
<UPageAside>
<UContentNavigation
highlight
:navigation="navigation"
/>
</UPageAside>
</template>
<slot />
</UPage>
</UContainer>
</template>
<template>
<UContainer>
<UPage>
<template #left>
<UAside>
<UNavigationTree :links="mapContentNavigation(navigation)" />
</UAside>
</template>
<slot />
</UPage>
</UContainer>
</template>
Divider 改名为 SeparatorfindPageHeadline 必须从 #ui-pro/utils/content 导入PageBody 组件不再存在 prose 属性如果您使用 Nuxt Studio 编辑文档,也需要迁移相关代码。
Studio 模块已废弃,新的通用 Preview API 已内嵌于 Nuxt Content,您可移除依赖及 nuxt.config.ts 中的 @nuxthq/studio 模块,只需在 Nuxt 配置中启用预览模式并绑定 Studio API。
export default defineNuxtConfig({
content: {
preview: {
api: 'https://api.nuxt.studio'
}
},
})
要保持 app 配置文件可由 Studio 更新,请将 nuxt.schema.ts 文件中的辅助导入由 @nuxthq/studio/theme 切换为 @nuxt/content/preview。