Skip to content

a-first-look-at-nuxt-ui-v4

初探 Nuxt UI v4

Nuxt UI v4 熱騰騰釋出囉!੭ ˙ᗜ˙ )੭

期待很久了,終於等到這一天,馬上來研究看看 ლ(´∀`ლ)

研究目標

雖然 Quasar 目前滿足大部分情境,但是 Quasar 更新放緩,作者心力分散至 Quasar CLI、SSR 開發,許多 UI 相關 issue 排不進排程,隨著需求與限制增加,可能會遇到瓶頸

而我目前僅用到 UI,至於為甚麼只用到 UI,以下是我的考量:

  • Electron、Capacitor 整合:

    Quasar 雖然可以整合 Electron、Capacitor,但是截至目前的開發經驗,依照需求直接使用 Electron、Capacitor 會更好

    因為 Quasar CLI 二次封裝後,不但更難除錯(很難知道是 CLI 有問題、還是框架有問題),且封裝後許多設定參數脫離原框架的文件,很難找到參考資料

  • SSR:

    我比較信任更成熟的 Nuxt,畢竟 SSR、SSG 是一個很複雜的問題,Nuxt 團隊有更多資源與人力可以解決問題

Nuxt UI 除了有官方背景外,還深度整合 Nuxt,潛力無窮,順便解決以下問題:

  • Quasar utils class 衝突問題(因為統一使用 Tailwind CSS 了)
  • Design Token 分散問題(雖然這個已經靠 UnoCSS 解決)
  • SSR 整合問題(Quasar + Nuxt 有時候會遇到一些水合小毛病)

背景資料

基於 Reka UITailwind CSSTailwind Variants 開發,內部還整合了 VueUse、多個 unplugin 套件與資源,滿滿自家人的概念 (◐‿◑)

還有以下特點:

  • MIT 授權
  • a11y 完善
  • VueUse 深度整合
  • Nuxt SSR 整合
  • Vue i18n 整合
  • 元件超多

重點筆記

元件特點

以下筆記主要相對於 Quasar UI 出發。

ui prop

Nuxt UI:Customize components

vue
<template>
  <UButton
    trailing-icon="i-lucide-chevron-right"
    size="md"
    color="neutral"
    variant="outline"
    :ui="{
      trailingIcon: 'rotate-90 size-3',
    }"
  >
    Button
  </UButton>
</template>

可以注入 class 至元件指定層級 DOM 中,這個功能與 PrimeVue 的 passthrough 類似

雖然實務開發上不是甚麼大問題,不過我自己有一個小疑問:基於 BEM 規範的 UI 套件若要魔改樣式,因為命名結構明確,可以很容易指向目標,但這類基於 Tailwind CSS 的 UI 套件若 ui 參數沒有開到,要指向特定的 DOM 是不是就需要相對複雜的 CSS 選擇器?

Form

欄位驗證可以直接用 Zod(支援 Standard Schema 的驗證器都可以)

配合 ts-rest 更簡單惹!(ゝ∀・)b

Nuxt UI:Form

Icon

內部使用 unplugin-icons超多 icon 可以用!੭ ˙ᗜ˙ )੭

會自動對 icon 進行按需引入(On-demand),不會載一大包字型檔案

icon

SSR、SSG 友善,不會有 FOUC 問題

Dashboard

一系列 Dashboard 元件,可以輕鬆拼出專業後台

Nuxt UI:Dashboard

Page

一系列 Page 元件,幾乎內建 Landing 頁面會用到的基礎元件了

綜合 Dashboard 元件,Nuxt UI 用來開發前後台都很適合

Nuxt UI:Page

Image

沒有額外封裝 img 元件,不過有提供一個 ColorModeImage,自動依照明暗模式切換圖片

若有安裝 @nuxt/image 會在內部自動依賴 NuxtImg 元件

Nuxt UI:ColorModeImage

Table

基於 TanStack Table 開發功能強大

cell 內容同時支援 slot 與 h function 更彈性

Nuxt UI:Table

Quasar 是 Dialog,在 Nuxt UI 中叫做 Modal

Nuxt UI:Modal

開啟方式不同於 Quasar,可以不用傳遞額外變數控制開啟(有需要的話還是可以綁定變數)

在 default slot 放入按鈕,即可以開啟,內容則寫在 content slot 中

vue
<template>
  <UModal>
    <UButton label="Open" color="neutral" variant="subtle" />

    <template #content>
      <Placeholder class="h-48 m-4" />
    </template>
  </UModal>
</template>

Design System

承襲 Tailwind CSS v4 設計,設定變成基於 CSS 變數

Nuxt UI:Design System

Figma Kit

所有的顏色、樣式幾乎都已用 token 定義,與 CSS 名稱一致,可以直接複製貼上,元件也有定義相關變體,可以直接切換

這個資源很讚,這樣 UI 或 PM 要出設計稿或草稿時,都可以直接使用、不用重畫,更不容易出現認知落差。(・∀・)9

Nuxt UI:Figma Kit

Template

可以從模板借鑒抄襲內容過來用 (≖‿ゝ≖)✧

Nuxt UI:Templates

AI 整合

MCP Server

可以與 AI 工具整合,甚至 ChatGPT 也可以用

Nuxt UI:MCP Server

安裝完成後使用指令(輸入 /)會出現以下提示:

mcp

  • find_component_for_usecase: Find the best component for your specific use

    給它功能情境,會幫你從 Nuxt UI 元件庫中挑最適合的元件(或元件組合),並說明為什麼、需要搭配哪些 props/slots/events、還會附上簡短使用片段。

  • implement_component_with_props: Generate complete component implementation with proper 當你已經決定「要用哪個 Nuxt UI 元件」與「要怎麼配置 props/事件/slots」,這個 prompt 會直接產出完整可用的 SFC 實作(含 <script setup> 型別、狀態、事件、slot 範例、a11y 小細節)。

  • setup_project_with_template: Get guided setup instructions for project

    引導式教學,教你怎麼設定環境

LLMs.txt

可以在詢問時提及 LLMs.txt URL,簡單來說就是一個適合 AI 閱讀文件,可以讓 AI 回應更精確

以下是 GPT-5 的問答結果:

Table 提問:

llm-table

Modal 提問:

llm-modal

配合 MCP 與 LLMs.txt 可以有效降低 Nuxt UI 入門門檻 ✧⁑。٩(ˊᗜˋ*)و✧⁕。

問題討論

顯示引入元件

底層是 unplugin-auto-importunplugin-vue-components,印象中可以這樣導入:

auto-import

不知道為甚麼不行,直接從 @nuxt/ui 引入也不行

不過還好可以根據目錄引入元件

ts
import UModal from '@nuxt/ui/components/Modal.vue'

useToast 位置不能單一設定

TIP

感謝狐狸大大的補充 (*´∀`)~♥

Toast 位置只能全域設定,不能像 Quasar 那樣每個 Toast 可以獨立設定位置

官方說 Headless UI 和 Radix Vue 都不支援單一設定,目前沒有計畫支援 (´・ω・`)

不過實際上影響不大,沒事不會一直換位置 XD

useOverlay

文件預期用來開啟 Modal 元件,這樣的缺點是需建立一個被開啟元件的 Modal 變體,使用上沒那麼方便,所以同先前 Quasar Dialog 的封裝,我也做了一個 openUsingModal function。

typescript
import type { Component } from 'vue'
import type { ComponentProps, ComponentSlots } from 'vue-component-type-helpers'
import UModal from '@nuxt/ui/components/Modal.vue'
import { h } from 'vue'

type ModelProps = ComponentProps<typeof UModal>

/** 將 Vue SFC 元件包装為 UModal,可以更簡單配合 useOverlay 使用
 *
 * @param component Vue SFC 元件
 * @param props SFC 內所有參數,包含 class、style、event 等等
 * @param modalProps UModal 原本參數
 * @param slots SFC 插槽
 */
function wrapWithModal<Comp extends Component>(
  component: Comp,
  props?: ComponentProps<Comp>,
  modalProps?: ModelProps,
  slots?: ComponentSlots<Comp>,
) {
  return h(UModal, {
    class: 'w-auto!',
    ...modalProps,
  }, {
    body: () => h(component, props ?? {}, slots ?? {}),
  })
}

/** 使用 useOverlay + UModal 開啟元件
 *
 * @param component Vue SFC 元件
 * @param props SFC 內所有參數,包含 class、style、event 等等
 * @param modalProps UModal 原本參數
 * @param slots SFC 插槽
 *
 * @example
 * ```typescript
 * const dialog = openUsingModal(BrandEditStatusForm, {
 *   data,
 *   onSuccess() {
 *     dialog.hide();
 *     handleEditSuccess();
 *   },
 * });
 * ```
 */
export function openUsingModal<Comp extends Component>(
  component: Comp,
  props?: ComponentProps<Comp>,
  modalProps?: ModelProps,
  slots?: ComponentSlots<Comp>,
) {
  const overlay = useOverlay()

  const modal = overlay.create(
    wrapWithModal(
      component,
      props,
      {
        close: false,
        ui: { body: 'p-0!' },
        ...modalProps,
      },
      slots,
    ),
    { props },
  )
  modal.open()

  return modal
}

沒有簡易所見及所得編輯器

需要額外引入,目前候選為:

  • Tiptap:MIT
  • TinyMCE:GPLv2+

TinyMCE 功能強大,不過 GPLv2+ 授權不太適合商業專案

自定義 Form 欄位

Quasar 可以使用 useFormChildField 元件包裝,自定義 Form 內的驗證欄位

研究了一下,其實有提供 useFormField,只是不知道為甚麼文件網頁沒有出現,但文件原始碼有此 md 檔

沒有 Time Picker

官方說沒有計畫。...(›´ω`‹ )

欸不是,都有 Calendar 了,做個 Time Picker 不過份吧!ლ(´口`ლ)

只能自己拼一個或是用 input 將 type 設為 time 版本

不過原生的 time input 在 window 的 chrome 上有點醜就是了 (。-`ω´-)

vue
<UInput
  v-model="data"
  type="time"
/>

input-time

總結 🐟

  • Nuxt UI v4 元件多、功能強大且免費,潛力無窮
  • Nuxt 深度整合,SSR、SSG 友善
  • VueUse、Vue i18n、unplugin 等等 Vue 生態系深度整合,可以省去不少設定時間
  • AI 整合(MCP、LLMs.txt),降低入門門檻

以上就是這次初探,其他細節就要等到實務開發時再慢慢研究了 ◝( •ω• )◟

Nuxt UI v4 整體感覺很不錯,不過有一個小小遺憾,就是不能使用 UnoCSS ( ˘・з・)

雖然有基於 UnoCSS 的 Una UI,不過考量生態系問題,還是先用 Nuxt UI 比較實際