Nuxt UI 4 Dashboard 實戰

Nuxt UI 的 Dashboard 元件把側邊欄狀態、響應式與收合行為都做掉了,剩下要自己決定的 只有幾件。這篇記三個做錯不會報錯的地方:漏了根元件、用 v-if 切版面、以及扁平化的元件命名。

系列・Nuxt 4 全棧實戰筆記・第 2/12 篇

用 Nuxt UI 做後台版面,大部分力氣不會花在刻畫面上——側邊欄的收合、拖曳調寬、行動版的抽屜行為,元件都做掉了。

剩下要自己決定的事不多,但其中三個做錯時不會有任何錯誤訊息。這篇只記這三個。

一、漏掉根元件,Toast 與 Tooltip 靜默失效

UApp 不只是個容器,它承載了整組疊加層:通知、提示氣泡、Modal 的掛載點、以及元件內建文字的語系。

沒有包它的話,useToast() 呼叫下去不會拋錯,只是什麼都不會出現。這個症狀最惡劣的地方在於它會被誤判成「通知邏輯沒被觸發」,於是接下來一小時都在查錯的那一端。

<script setup lang="ts">
  import { zh_tw } from '@nuxt/ui/locale'
</script>

<template>
  <UApp :locale="zh_tw">
    <NuxtPage />
  </UApp>
</template>

順帶一提,locale 傳進去之後,元件內建的文字(像下拉選單的「無結果」)才會是中文——這是少數幾個「不設定就是英文」而不是「不設定就壞掉」的地方,很容易上線後才被使用者發現。

二、桌機版與行動版不要用 v-if 切

同一組導覽,桌機是常駐側邊欄、行動版是抽屜。最直覺的寫法是依螢幕寬度切換兩個元件:

<!-- 螢幕跨過斷點時,元件被銷毀重建 -->
<UDashboardSidebar v-if="!isMobile" />
<UDashboardSidebarMobile v-else />

問題不在渲染結果,在於 v-if 會銷毀元件實例。使用者調整視窗大小、或裝置旋轉,側邊欄的展開狀態、捲動位置、甚至展開中的子選單就全部歸零。開發時很少轉螢幕,所以這個問題通常由使用者回報。

你可能會想:那包一層快取元件的機制不就解決了?可以,但你等於為了修一個不該存在的問題,多引入一套需要自己管理生命週期的東西——而且它保住的只是元件狀態,DOM 該有的捲動位置與焦點仍然會掉。真正的問題是這裡本來就不需要兩個元件。

用 CSS 控制顯示就沒有這件事——元件一直活著,只是有沒有被畫出來:

<UDashboardSidebar collapsible resizable class="hidden lg:flex" />

同樣的道理,UDashboardGroup 的 unit 建議設 rem:側邊欄寬度跟著使用者的字級縮放走,而不是釘死在像素上。

三、選單資料要有型別

導覽選單是最容易被寫成「一坨物件陣列」的地方,而它同時也是最常被改的地方。用官方的型別標注起來,打錯的屬性名當場就會被抓到:

import type { NavigationMenuItem } from '@nuxt/ui'

const menuItems: NavigationMenuItem[] = [
  { label: '儀表板', icon: 'i-lucide-layout-dashboard', to: '/' },
  {
    label: '使用者',
    icon: 'i-lucide-users',
    children: [
      { label: '列表', to: '/users' },
      { label: '新增', to: '/users/new' },
    ],
  },
]

沒有型別的話,icon 打成 iconName 這種錯誤不會有人告訴你——圖示只是沒出現,而版面照樣是完整的。

這三個的共同點

做錯你看到的你會去查的
沒包 UApp通知沒跳出來通知的觸發邏輯
用 v-if 切版面狀態偶爾歸零狀態管理
選單沒有型別圖示沒出現圖示名稱、icon 套件

三個的實際原因都不在你會去查的那一欄。這也是為什麼它們值得先寫下來——它們不是難,是不會叫。