Lzh on GitHub

迁移

将您的应用程序从 Nuxt UI v2 迁移到 Nuxt UI v3 的综合指南。

Nuxt UI v3.0 是一个从头开始重建的新主要版本,引入了现代架构,具有显著的性能改进和增强的开发者体验。此主要版本包含多项重大更改以及强大的新功能:

  • Tailwind CSS v4:从 JavaScript 迁移到基于 CSS 的配置
  • Reka UI:取代 Headless UI 作为底层组件库
  • Tailwind Variants:用于组件变体的新样式 API

本指南提供了将您的应用程序从 v2 迁移到 v3 的分步说明。

迁移您的项目

更新 Tailwind CSS

Tailwind CSS v4 对其配置方法进行了重大更改。官方的 Tailwind 升级工具将帮助自动化大部分迁移过程。

有关所有更改的详细说明,请参阅官方的 Tailwind CSS v4 升级指南
  1. 创建 main.css 文件并将其导入到您的 nuxt.config.ts 文件中:
@import "tailwindcss";
  1. 运行 Tailwind CSS 升级工具:
npx @tailwindcss/upgrade

更新 Nuxt UI

  1. 安装最新版本的包:
pnpm add @nuxt/content
pnpm add @nuxt/ui-pro
  1. 将其导入到您的 CSS 中:
app/assets/css/main.css
@import "tailwindcss";
@import "@nuxt/ui";
app/assets/css/main.css
@import "tailwindcss";
@import "@nuxt/ui-pro";
  1. 使用 App 组件包裹您的应用程序:
  1. 在您的 nuxt.config.ts 文件中添加 @nuxt/ui-pro 模块,因为它不再是一个层:
nuxt.config.ts
export default defineNuxtConfig({
-  extends: ['@nuxt/ui-pro'],
-  modules: ['@nuxt/ui']
+  modules: ['@nuxt/ui-pro']
})
  1. 使用 App 组件包裹您的应用程序:
app.vue
<template>
  <UApp>
    <NuxtPage />
  </UApp>
</template>

v2 变更

现在您已经更新了项目,可以开始迁移代码了。以下是 Nuxt UI v3 中所有重大更改的全面列表。

更新后的设计系统

在 Nuxt UI v2 中,我们混合使用了带有 primarygrayerror 别名以及 Tailwind CSS 中所有颜色的 设计系统。我们已将其替换为带有 7 个颜色别名的适当设计系统:

颜色默认值描述
primarygreen主要品牌颜色,用作组件的默认颜色。
secondaryblue辅助颜色以补充主要颜色。
successgreen用于成功状态。
infoblue用于信息状态。
warningyellow用于警告状态。
errorred用于表单错误验证状态。
neutralslate背景、文本等的中性颜色。

此更改引入了您需要注意的几项重大更改:

  • gray 颜色已重命名为 neutral
<template>
- <p class="text-gray-500 dark:text-gray-400" />
+ <p class="text-neutral-500 dark:text-neutral-400" />
</template>
您还可以使用新的 设计令牌 来处理浅色和深色模式:
<template>
- <p class="text-gray-500 dark:text-gray-400" />
+ <p class="text-muted" />

- <p class="text-gray-900 dark:text-white" />
+ <p class="text-highlighted" />
</template>
  • color 属性中的 grayblackwhite 已被删除,取而代之的是 neutral
- <UButton color="black" />
+ <UButton color="neutral" />

- <UButton color="gray" />
+ <UButton color="neutral" variant="subtle" />

- <UButton color="white" />
+ <UButton color="neutral" variant="outline" />
  • 您不再可以在 color 属性中使用 Tailwind CSS 颜色,请改用新的别名:
- <UButton color="red" />
+ <UButton color="error" />
了解如何扩展设计系统以添加新的颜色别名。
  • app.config.ts 中的颜色配置已移动到 colors 对象中:
export default defineAppConfig({
  ui: {
-   primary: 'green',
-   gray: 'cool'
+   colors: {
+     primary: 'green',
+     neutral: 'slate'
+   }
  }
})

更新后的主题系统

Nuxt UI 组件现在使用 Tailwind Variants API 进行样式设置,这使得您使用 app.config.tsui 属性进行的所有覆盖都已过时。

export default defineAppConfig({
   ui: {
     button: {
-       font: 'font-bold',
-       default: {
-         size: 'md',
-         color: 'primary'
-       }
+       slots: {
+         base: 'font-medium'
+       },
+       defaultVariants: {
+         size: 'md',
+         color: 'primary'
+       }
     }
   }
})
  • 更新您的 ui props 以使用其新主题覆盖每个组件的插槽:
<template>
- <UButton :ui="{ font: 'font-bold' }" />
+ <UButton :ui="{ base: 'font-bold' }" />
</template>
我们无法在此处详细说明所有更改,但您可以在 “主题” 部分查看每个组件的主题。

重命名组件

我们已重命名了一些 Nuxt UI 组件,以符合 Reka UI 命名约定:

v2v3
DividerSeparator
DropdownDropdownMenu
FormGroupFormField
RangeSlider
ToggleSwitch
NotificationToast
VerticalNavigationNavigationMenu with orientation="vertical"
HorizontalNavigationNavigationMenu with orientation="horizontal"

以下是已重命名或删除的 Nuxt UI Pro 组件:

v1v3
BlogListBlogPosts
ColorModeToggleColorModeSwitch
DashboardCardRemoved (use PageCard instead)
DashboardLayoutDashboardGroup
DashboardModalRemoved (use Modal instead)
DashboardNavbarToggleDashboardSidebarToggle
DashboardPageRemoved
DashboardPanelContentRemoved (use #body slot instead)
DashboardPanelHandleDashboardResizeHandle
DashboardSectionRemoved (use PageCard instead)
DashboardSidebarLinksRemoved (use NavigationMenu instead)
DashboardSlideoverRemoved (use Slideover instead)
FooterLinksRemoved (use NavigationMenu instead)
HeaderLinksRemoved (use NavigationMenu instead)
LandingCardRemoved (use PageCard instead)
LandingCTAPageCTA
LandingFAQRemoved (use PageAccordion instead)
LandingGridRemoved (use PageGrid instead)
LandingHeroRemoved (use PageHero instead)
LandingLogosPageLogos
LandingSectionPageSection
LandingTestimonialRemoved (use PageCard instead)
NavigationAccordionContentNavigation
NavigationLinksContentNavigation
NavigationTreeContentNavigation
PageErrorError
PricingCardPricingPlan
PricingGridPricingPlans
PricingSwitchRemoved (use Switch or Tabs instead)

更改的组件

除了重命名的组件,组件 API 还有很多更改。让我们详细说明最重要的更改:

  • linksoptions 属性已重命名为 items 以保持一致性:
<template>
- <USelect :options="countries" />
+ <USelect :items="countries" />

- <UHorizontalNavigation :links="links" />
+ <UNavigationMenu :items="links" />
</template>
此更改影响以下组件:BreadcrumbHorizontalNavigationInputMenuRadioGroupSelectSelectMenuVerticalNavigation
  • 不同组件中的 click 字段已删除,取而代之的是原生的 Vue onClick 事件:
<script setup lang="ts">
const items = [{
  label: 'Edit',
-  click: () => {
+  onClick: () => {
    console.log('Edit')
  }
}]
</script>
此更改影响 Toast 组件以及所有具有 items 链接的组件,例如 NavigationMenuDropdownMenuCommandPalette 等。
  • 全局的 ModalsSlideoversNotifications 组件已删除,取而代之的是 App 组件:
app.vue
<template>
+  <UApp>
+    <NuxtPage />
+  </UApp>
-  <UModals />
-  <USlideovers />
-  <UNotifications />
</template>
  • v-model:open 指令和 default-open 属性现在用于控制可见性:
<template>
- <UModal v-model="open" />
+ <UModal v-model:open="open" />
</template>
此更改影响以下组件:ContextMenuModalSlideover,并启用对 InputMenuSelectSelectMenuTooltip 的可见性控制。
  • 默认插槽现在用于触发器,内容放在 #content 插槽内部(您不需要使用 v-model:open 指令):
<script setup lang="ts">
- const open = ref(false)
</script>

<template>
- <UButton label="Open" @click="open = true" />

- <UModal v-model="open">
+ <UModal>
+   <UButton label="Open" />

+   <template #content>
      <div class="p-4">
        <Placeholder class="h-48" />
      </div>
+   </template>
  </UModal>
</template>
此更改影响以下组件:ModalPopoverSlideoverTooltip
  • #header#body#footer 插槽已添加到 #content 插槽内部,类似于 Card 组件:
<template>
- <UModal>
+ <UModal title="Title" description="Description">
-   <div class="p-4">
+   <template #body>
      <Placeholder class="h-48" />
+   </template>
-   </div>
  </UModal>
</template>
此更改影响以下组件:ModalSlideover

更改的组合式函数

  • useToast() 组合式函数的 timeout 属性已重命名为 duration
<script setup lang="ts">
const toast = useToast()

- toast.add({ title: 'Invitation sent', timeout: 0 })
+ toast.add({ title: 'Invitation sent', duration: 0 })
</script>
  • useModaluseSlideover 组合式函数已被移除,取而代之的是更通用的 useOverlay 组合式函数:

一些重要的区别:

  • useOverlay 组合式函数现在用于创建覆盖层实例
  • 开启的覆盖层可以等待其结果
  • 覆盖层不能再使用 modal.close()slideover.close() 关闭,而是自动关闭:当从开启的组件中明确触发 close 事件时,或者当覆盖层自身关闭时(点击背景、按 ESC 键等)
  • 要在父组件中捕获返回值,您必须明确发出一个带有所需值的 close 事件
<script setup lang="ts">
import { ModalExampleComponent } from '#components'

- const modal = useModal()
+ const overlay = useOverlay()

- modal.open(ModalExampleComponent)
+ const modal = overlay.create(ModalExampleComponent)
</script>

属性现在通过 props 属性传递:

<script setup lang="ts">
import { ModalExampleComponent } from '#components'

- const modal = useModal()
+ const overlay = useOverlay()

const count = ref(0)

- modal.open(ModalExampleComponent, {
-   count: count.value
- })
+ const modal = overlay.create(ModalExampleComponent, {
+   props: {
+     count: count.value
+   }
+ })
</script>

现在通过 close 事件关闭模态框。modal.open 方法现在返回一个实例,该实例可用于在模态框关闭时等待模态框的结果:

<script setup lang="ts">
import { ModalExampleComponent } from '#components'

- const modal = useModal()
+ const overlay = useOverlay()

+ const modal = overlay.create(ModalExampleComponent)

- function openModal() {
-   modal.open(ModalExampleComponent, {
-     onSuccess() {
-       toast.add({ title: 'Success!' })
-     }
-   })
- }
+ async function openModal() {
+   const instance = modal.open(ModalExampleComponent, {
+     count: count.value
+   })
+
+   const result = await instance.result
+
+   if (result) {
+     toast.add({ title: 'Success!' })
+   }
+ }
</script>

更改了表单验证

用于定位表单字段的错误对象属性已从 path 重命名为 name

<script setup lang="ts">
const validate = (state: any): FormError[] => {
  const errors = []
  if (!state.email) {
    errors.push({ 
-     path: 'email', 
+     name: 'email',
      message: 'Required' 
    })
  }
  if (!state.password) {
    errors.push({ 
-     path: 'password', 
+     name: 'password',
      message: 'Required' 
    })
  }
  return errors
}
</script>
此页面正在施工中,我们将定期改进。