迁移到 v3

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

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

  • 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/ui
  1. 在您的 CSS 中导入
app/assets/css/main.css
@import "tailwindcss";
@import "@nuxt/ui";
  1. App 组件包裹您的应用程序
app.vue
<template>
  <UApp>
    <NuxtPage />
  </UApp>
</template>

v2 中的更改

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

更新了设计系统

在 Nuxt UI v2 中,我们有一个混合的设计系统,其中包含 primarygrayerror 别名和所有 Tailwind CSS 颜色。我们已将其替换为带有 7 种颜色别名的适当设计系统

颜色默认值描述
primary绿色主要品牌颜色,用作组件的默认颜色。
辅助蓝色补充主色的辅助颜色。
成功绿色用于成功状态。
信息蓝色用于信息状态。
警告黄色用于警告状态。
error红色用于表单错误验证状态。
neutral板岩用于背景、文本等的中性颜色。

此更改引入了一些您需要注意的重大更改

  • 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 属性以使用新主题覆盖每个组件的插槽
<template>
- <UButton :ui="{ font: 'font-bold' }" />
+ <UButton :ui="{ base: 'font-bold' }" />
</template>
我们无法在此处详细说明所有更改,但您可以在“主题”部分查看每个组件的主题。

重命名组件

我们重命名了一些 Nuxt UI 组件,以与 Reka UI 命名约定保持一致。

v2v3
DividerSeparator
DropdownDropdownMenu
FormGroupFormField
RangeSlider
ToggleSwitch
NotificationToast
VerticalNavigationNavigationMenuorientation="vertical"
HorizontalNavigationNavigationMenuorientation="horizontal"

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

v1v3
BlogList博客文章列表
ColorModeToggle颜色模式开关
DashboardCard已移除(请改用 PageCard
DashboardLayout仪表盘组
DashboardModal已移除(请改用 Modal
DashboardNavbarToggle仪表盘侧边栏切换
DashboardPage已移除
DashboardPanelContent已移除(请改用 #body 插槽)
DashboardPanelHandle仪表盘调整大小手柄
DashboardSection已移除(请改用 PageCard
DashboardSidebarLinks已移除(请改用 NavigationMenu
DashboardSlideover已移除(请改用 Slideover
FooterLinks已移除(请改用 NavigationMenu
HeaderLinks已移除(请改用 NavigationMenu
LandingCard已移除(请改用 PageCard
LandingCTA页面 CTA
LandingFAQ已移除(请改用 Accordion
LandingGrid已移除(请改用 PageGrid
LandingHero已移除(请改用 PageHero
LandingLogos页面标志
LandingSection页面节
LandingTestimonial已移除(请改用 PageCard
NavigationAccordion内容导航
NavigationLinks内容导航
NavigationTree内容导航
PageError错误
PricingCard定价方案
PricingGrid定价方案列表
PricingSwitch已移除(请改用 SwitchTabs

更改的组件

除了重命名的组件,组件 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
  • #content 插槽内部添加了 #header#body#footer 插槽,与 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>

此页面正在开发中,我们将定期改进它。