初探 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 UI、Tailwind CSS 與 Tailwind Variants 開發,內部還整合了 VueUse、多個 unplugin
套件與資源,滿滿自家人的概念 (◐‿◑)
還有以下特點:
- MIT 授權
- a11y 完善
- VueUse 深度整合
- Nuxt SSR 整合
- Vue i18n 整合
- 元件超多
重點筆記
元件特點
以下筆記主要相對於 Quasar UI 出發。
ui prop
<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
Icon
內部使用 unplugin-icons,超多 icon 可以用!੭ ˙ᗜ˙ )੭
會自動對 icon 進行按需引入(On-demand),不會載一大包字型檔案
SSR、SSG 友善,不會有 FOUC 問題
Dashboard
一系列 Dashboard 元件,可以輕鬆拼出專業後台
Page
一系列 Page 元件,幾乎內建 Landing 頁面會用到的基礎元件了
綜合 Dashboard 元件,Nuxt UI 用來開發前後台都很適合
Image
沒有額外封裝 img 元件,不過有提供一個 ColorModeImage
,自動依照明暗模式切換圖片
若有安裝 @nuxt/image
會在內部自動依賴 NuxtImg
元件
Table
基於 TanStack Table 開發功能強大
cell 內容同時支援 slot 與 h function 更彈性
Modal
Quasar 是 Dialog,在 Nuxt UI 中叫做 Modal
開啟方式不同於 Quasar,可以不用傳遞額外變數控制開啟(有需要的話還是可以綁定變數)
在 default slot 放入按鈕,即可以開啟,內容則寫在 content slot 中
<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 變數
Figma Kit
所有的顏色、樣式幾乎都已用 token 定義,與 CSS 名稱一致,可以直接複製貼上,元件也有定義相關變體,可以直接切換
這個資源很讚,這樣 UI 或 PM 要出設計稿或草稿時,都可以直接使用、不用重畫,更不容易出現認知落差。(・∀・)9
Template
可以從模板借鑒抄襲內容過來用 (≖‿ゝ≖)✧
AI 整合
MCP Server
可以與 AI 工具整合,甚至 ChatGPT 也可以用
安裝完成後使用指令(輸入 /)會出現以下提示:
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 提問:
Modal 提問:
配合 MCP 與 LLMs.txt 可以有效降低 Nuxt UI 入門門檻 ✧⁑。٩(ˊᗜˋ*)و✧⁕。
問題討論
顯示引入元件
底層是 unplugin-auto-import
與 unplugin-vue-components
,印象中可以這樣導入:
不知道為甚麼不行,直接從 @nuxt/ui
引入也不行
不過還好可以根據目錄引入元件
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。
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 可以使用 useFormChild
或 Field
元件包裝,自定義 Form 內的驗證欄位
研究了一下,其實有提供 useFormField
,只是不知道為甚麼文件網頁沒有出現,但文件原始碼有此 md 檔。
沒有 Time Picker
官方說沒有計畫。...(›´ω`‹ )
欸不是,都有 Calendar 了,做個 Time Picker 不過份吧!ლ(´口`ლ)
只能自己拼一個或是用 input 將 type 設為 time 版本
不過原生的 time input 在 window 的 chrome 上有點醜就是了 (。-`ω´-)
<UInput
v-model="data"
type="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 比較實際