在 Nuxt 中使用 YH-UI
本指南将帮助您在 Nuxt 3/4 项目中集成和使用 YH-UI 组件库。
安装
1. 安装依赖
pnpm add @yh-ui/nuxt
# 或使用 npm
npm install @yh-ui/nuxt
# 或使用 yarn
yarn add @yh-ui/nuxt2. 注册模块
在 nuxt.config.ts 中添加 @yh-ui/nuxt 模块:
export default defineNuxtConfig({
modules: [
'@yh-ui/nuxt'
]
})核心优势
为什么选择 YH-UI 与 Nuxt 配合使用?
- 🚀 SSR 零配置支持:所有组件均经过深度 SSR 优化,确保 HTML 在服务端生成,提升 SEO 和首屏加载速度,且无需繁琐配置。
- 🧩 语义化自动导入:模块自动注册所有
Yh组件、Composables 和全局方法。你只管写代码,IDE 提供完美提示,无需任何import。 - ⚡ 性能极致优化:支持 Tree Shaking,配合 Nuxt 的代码分割机制,仅打包你真正使用的组件,让包体积维持在最小。
- 🔒 状态隔离与安全:针对 SSR 环境下的全局状态污染风险,我们提供了请求级的
useZIndex和useId隔离机制,确保多用户并发访问的安全。 - 🎨 样式按需加载:样式系统支持 SCSS/CSS 按需注入,支持与 Nuxt 主题系统深度集成。
注意事项 (Precautions)
在使用过程中,请务必关注以下几点以避免常见的 SSR 陷阱:
1. 客户端专用逻辑 (Client-only)
由于代码会在服务端和客户端同时执行,直接访问 window、document 或 localStorage 会导致服务端报错。
- 推荐做法:使用
onMounted钩子或 Nuxt 提供的import { isClient } from '@yh-ui/utils'进行环境检查。
2. 水合不匹配 (Hydration Mismatch)
如果服务端生成的 HTML 与客户端初次渲染的 HTML 不一致(例如直接在 setup 中生成随机数或获取实时时间并渲染),会导致 Vue 报错。
- 推荐做法:确保渲染数据的一致性,或使用
<ClientOnly>组件包裹动态内容。
3. 组件 Ref 获取
在 Nuxt 中,建议使用 ref<InstanceType<typeof YhButton>>() 获取组件实例,以获得最佳的类型支持。
您可以通过 yhUI 配置键来自定义模块行为:
export default defineNuxtConfig({
modules: ['@yh-ui/nuxt'],
yhUI: {
// 是否自动导入样式(默认: true)
importStyle: true
}
})可用配置
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
importStyle | boolean | true | 是否自动导入组件样式 |
prefix | string | 'Yh' | 组件前缀,例如设置为 My 则组件名为 MyButton |
buildTranspile | boolean | true | 是否自动转译相关依赖 |
自动导入
组件自动导入
所有 Yh 开头的组件都会自动导入,无需手动注册:
<template>
<div>
<!-- 直接使用,无需导入 -->
<YhButton type="primary">点击我</YhButton>
<YhInput v-model="value" placeholder="输入内容" />
<YhSelect v-model="selected">
<YhOption label="选项1" value="1" />
<YhOption label="选项2" value="2" />
</YhSelect>
</div>
</template>
<script setup>
// 无需导入组件
const value = ref('')
const selected = ref('')
</script>Composables 自动导入
常用的 Hooks 也会自动导入:
<script setup>
// 直接使用,无需导入
const ns = useNamespace('my-component')
const id = useId()
const { nextZIndex } = useZIndex()
// 生成 BEM 类名
const className = computed(() => ns.b()) // 'yh-my-component'
const blockClass = computed(() => ns.b('header')) // 'yh-my-component-header'
</script>可用的 Composables:
useNamespace- BEM 类名生成useId- 唯一 ID 生成useZIndex- z-index 管理useLocale- 国际化useFormItem- 表单项集成useGlobalNamespace- 全局命名空间
全局方法自动导入
Message 和 Notification 方法也会自动导入:
<script setup>
const showSuccess = () => {
// 直接使用,无需导入
YhMessage.success('操作成功!')
}
const showNotification = () => {
YhNotification({
title: '通知标题',
message: '这是通知内容',
type: 'success'
})
}
</script>SSR 支持
YH-UI 完全支持 Server-Side Rendering (SSR),所有组件都经过了 SSR 兼容性测试。
SSR 安全性
✅ 完全 SSR 安全的功能
- 所有基础组件渲染
- Props 和事件绑定
- 样式应用
- BEM 类名生成
- ID 生成(使用 Vue 3.5 原生
useId) - z-index 管理(应用级隔离)
🔒 客户端限制的功能
以下功能仅在客户端环境生效,SSR 阶段会被安全忽略:
- DOM 操作(如
focus(),blur()) - 浏览器事件监听(如
resize,scroll) window或document访问
Hydration 一致性
YH-UI 确保服务端和客户端渲染的 HTML 结构完全一致,避免 Hydration Mismatch 错误。
内部机制
- ID 生成: 使用 Vue 3.5+ 原生
useId()API - Z-Index 管理: 每个 SSR 请求拥有独立的计数器
- DOM 访问保护: 所有 DOM 操作都包裹在
onMounted中
最佳实践
1. 避免在 setup 中直接访问 DOM
<script setup>
// ❌ 错误:SSR 时 window 不存在
const width = window.innerWidth
// ✅ 正确:在 onMounted 中访问
const width = ref(0)
onMounted(() => {
width.value = window.innerWidth
})
</script>2. 使用 isClient / isServer 检查
<script setup>
import { isClient } from '@yh-ui/utils'
// ✅ 正确:条件执行
if (isClient) {
// 客户端专用逻辑
console.log('Running in browser')
}
</script>3. 异步数据获取
<script setup>
// ✅ 推荐:使用 Nuxt 的数据获取方法
const { data } = await useFetch('/api/data')
</script>完整示例
基础页面
<!-- pages/index.vue -->
<template>
<div class="container">
<h1>YH-UI + Nuxt 示例</h1>
<!-- 表单 -->
<YhForm :model="form" label-width="100px">
<YhFormItem label="用户名">
<YhInput v-model="form.username" placeholder="请输入用户名" />
</YhFormItem>
<YhFormItem label="密码">
<YhInput
v-model="form.password"
type="password"
show-password
placeholder="请输入密码"
/>
</YhFormItem>
<YhFormItem label="类型">
<YhSelect v-model="form.type" placeholder="请选择">
<YhOption label="类型1" value="1" />
<YhOption label="类型2" value="2" />
<YhOption label="类型3" value="3" />
</YhSelect>
</YhFormItem>
<YhFormItem>
<YhButton type="primary" @click="handleSubmit">
提交
</YhButton>
<YhButton @click="handleReset">
重置
</YhButton>
</YhFormItem>
</YhForm>
</div>
</template>
<script setup lang="ts">
// 组件和方法都是自动导入的
const form = reactive({
username: '',
password: '',
type: ''
})
const handleSubmit = () => {
YhMessage.success('提交成功!')
}
const handleReset = () => {
form.username = ''
form.password = ''
form.type = ''
}
</script>
<style scoped>
.container {
max-width: 600px;
margin: 0 auto;
padding: 40px 20px;
}
</style>带数据获取的页面
<!-- pages/users.vue -->
<template>
<div class="user-list">
<YhCard v-for="user in users" :key="user.id" class="user-card">
<template #header>
<div class="card-header">
<span>{{ user.name }}</span>
<YhTag :type="user.status">{{ user.status }}</YhTag>
</div>
</template>
<div class="user-info">
<p>邮箱: {{ user.email }}</p>
<p>电话: {{ user.phone }}</p>
</div>
<template #footer>
<YhButton type="primary" size="small" @click="editUser(user)">
编辑
</YhButton>
<YhButton type="danger" size="small" @click="deleteUser(user.id)">
删除
</YhButton>
</template>
</YhCard>
</div>
</template>
<script setup lang="ts">
// 使用 Nuxt 的数据获取 (SSR 友好)
const { data: users } = await useFetch('/api/users')
const editUser = (user) => {
YhNotification({
title: '编辑用户',
message: `正在编辑用户: ${user.name}`,
type: 'info'
})
}
const deleteUser = async (userid) => {
try {
await $fetch(`/api/users/${id}`, { method: 'DELETE' })
YhMessage.success('删除成功')
} catch (error) {
YhMessage.error('删除失败')
}
}
</script>
<style scoped>
.user-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 20px;
padding: 20px;
}
.user-card {
height: 100%;
}
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
</style>主题定制
全局配置
使用 ConfigProvider 组件进行全局配置:
<!-- app.vue -->
<template>
<YhConfigProvider :theme="theme" :locale="locale" size="default">
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</YhConfigProvider>
</template>
<script setup lang="ts">
const theme = ref({
primaryColor: '#6366f1',
borderRadius: '8px'
})
const locale = ref('zh-CN')
</script>CSS 变量覆盖
在全局样式中覆盖 CSS 变量:
/* assets/css/main.css */
:root {
/* 主色调 */
--yh-color-primary: #6366f1;
--yh-color-success: #10b981;
--yh-color-warning: #f59e0b;
--yh-color-danger: #ef4444;
/* 圆角 */
--yh-border-radius-base: 8px;
/* 字体 */
--yh-font-family: 'Inter', sans-serif;
}性能优化
Tree Shaking
YH-UI 支持 Tree Shaking,未使用的组件不会被打包:
<template>
<!-- 只使用 Button,其他组件不会被打包 -->
<YhButton>Click Me</YhButton>
</template>按需加载
对于大型组件(如 Table、Transfer),考虑在需要时动态导入:
<script setup>
// 动态导入(虽然组件已自动导入,但可以用于代码分割)
const showTree = ref(false)
const loadTreeSelect = async () => {
showTree.value = true
}
</script>
<template>
<div>
<YhButton @click="loadTreeSelect">加载树选择器</YhButton>
<YhTreeSelect v-if="showTree" v-model="value" :data="treeData" />
</div>
</template>类型支持
YH-UI 提供完整的 TypeScript 类型定义,享受类型提示和自动完成:
<script setup lang="ts">
import type { FormInstance } from '@yh-ui/components'
const formRef = ref<FormInstance>()
const validate = async () => {
try {
await formRef.value?.validate()
// 验证通过
} catch (error) {
// 验证失败
}
}
</script>
<template>
<YhForm ref="formRef" :model="form">
<!-- ... -->
</YhForm>
</template>常见问题
1. 组件未自动导入?
确保已正确安装并注册 @yh-ui/nuxt 模块:
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@yh-ui/nuxt'] // 确保此行存在
})2. 样式未加载?
检查配置中是否启用了样式导入:
export default defineNuxtConfig({
yhUI: {
importStyle: true // 确保为 true
}
})3. Hydration Mismatch 错误?
这通常是由于在 setup 中直接访问 window 或 document 导致的。请使用 onMounted:
<script setup>
// ❌ 错误
const width = ref(window.innerWidth)
// ✅ 正确
const width = ref(0)
onMounted(() => {
width.value = window.innerWidth
})
</script>4. 如何禁用某个组件的自动导入?
目前所有组件都会自动导入。如果需要精细控制,可以手动导入:
<script setup>
import { YhButton } from '@yh-ui/components'
// 显式导入会覆盖自动导入
</script>