前言
在开发 SonicToolLab 时,我发现用户对工具站的耐受度其实很低。
- 如果点击转换按钮后界面卡死,用户会以为网页坏了。
- 如果在晚上打开网页被亮瞎眼,用户会直接关闭。
- 如果输错网址看到原生丑陋的 404,用户会觉得这个站很“水”。
今天这一篇,我们不聊复杂的算法,只聊体验。如何利用 Nuxt UI 提供的组件,花 20% 的时间提升 80% 的质感。
🌗 1. 丝滑的深色模式 (Dark Mode)
Nuxt UI 基于 Tailwind CSS,天生支持深色模式。但要做到“好用”,有几个关键步骤。
步骤一:配置 Color Mode
虽然 Nuxt UI 默认集成了 @nuxtjs/color-mode,但我建议在 nuxt.config.ts 中显式配置一下,确保它使用 class 策略(给 html 标签加 dark 类名)。
// nuxt.config.ts
export default defineNuxtConfig({
colorMode: {
classSuffix: '', // 配合 Tailwind,去掉了默认的 -mode 后缀
preference: 'system', // 默认跟随系统
fallback: 'light' // 兜底策略
}
})
步骤二:封装切换组件
不要把切换逻辑散落在各处。我封装了一个通用的 <ColorModeButton />。
难点:Hydration Mismatch(水合不匹配)
服务端渲染时不知道用户的系统是黑还是白,可能会导致图标闪烁(服务端渲染了月亮,客户端变成了太阳)。
解法: 使用 <ClientOnly> 包裹按钮,或者使用 v-if 等待挂载。
Code snippet
<script setup>
const colorMode = useColorMode()
// 计算属性:判断当前是否为暗色
const isDark = computed({
get () {
return colorMode.value === 'dark'
},
set () {
// 切换模式
colorMode.preference = colorMode.value === 'dark' ? 'light' : 'dark'
}
})
</script>
<template>
<ClientOnly>
<UButton
:icon="isDark ? 'i-heroicons-moon-20-solid' : 'i-heroicons-sun-20-solid'"
color="gray"
variant="ghost"
aria-label="切换主题"
@click="isDark = !isDark"
/>
<template #fallback>
<div class="w-8 h-8" />
</template>
</ClientOnly>
</template>
⏳ 2. 拒绝白屏:Loading 状态与骨架屏
工具站经常需要请求 API(比如汇率、Whois 信息)。在数据回来之前,千万不要留白。
全局加载条
Nuxt 内置了 <NuxtLoadingIndicator />。在 app.vue 顶部加上它:
Code snippet
<template>
<NuxtLoadingIndicator color="#34d399" />
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</template>
这样,路由切换时顶部会有进度条,给用户“由于我在加载,请稍等”的反馈。
局部骨架屏 (Skeleton)
Nuxt UI 提供了 USkeleton 组件。配合 useFetch 的 status 非常好用。
Code snippet
<script setup>
const { data, status } = await useFetch('/api/tools/list', { lazy: true })
</script>
<template>
<div class="grid grid-cols-3 gap-4">
<template v-if="status === 'pending'">
<UCard v-for="i in 6" :key="i">
<div class="flex items-center gap-4">
<USkeleton class="h-12 w-12 rounded-full" />
<div class="space-y-2">
<USkeleton class="h-4 w-[200px]" />
<USkeleton class="h-4 w-[150px]" />
</div>
</div>
</UCard>
</template>
<template v-else>
<ToolCard v-for="tool in data" :tool="tool" />
</template>
</div>
</template>
这种体验比一个旋转的 Spinner 要高级得多,因为它提前勾勒出了页面的布局。
🚧 3. 自定义 404 页面
Nuxt 默认的错误页面是黑底白字的调试风,上线后如果用户输错网址看到这个,会非常出戏。
在项目根目录创建 error.vue(注意不是在 pages 里,是根目录)。
Code snippet
<script setup lang="ts">
const props = defineProps({
error: Object
})
const handleError = () => {
// 清除错误并跳回首页
clearError({ redirect: '/' })
}
</script>
<template>
<div class="h-screen flex flex-col items-center justify-center text-center p-4">
<h1 class="text-9xl font-bold text-primary-500">404</h1>
<p class="text-xl mt-4 text-gray-500 dark:text-gray-400">
糟糕,你仿佛来到了知识的荒原...
</p>
<p class="text-sm mt-2 mb-8 text-gray-400">
错误信息:{{ error?.message }}
</p>
<UButton
size="xl"
icon="i-heroicons-home"
@click="handleError"
>
带我回首页
</UButton>
</div>
</template>
这样,即使用户迷路了,也能通过一个友好的按钮找回方向,大大降低了跳出率。
总结
UI/UX 的打磨是没有尽头的,但在 SonicToolLab 的开发中,以上三点是性价比最高的投入:
- Dark Mode 照顾了开发者的眼睛(这是你的核心用户群)。
- Skeleton 缓解了等待的焦虑。
- Custom 404 挽留了迷路的用户。
把这些细节做好,你的网站就从“一个简单的 Demo”进化成了“一个成熟的产品”。