Nuxt.js useState #
一、useState概述 #
useState 是 Nuxt.js 提供的内置组合式函数,用于创建跨组件共享的响应式状态。它基于 Vue 的响应式系统,并支持 SSR。
1.1 特点 #
- 跨组件共享:状态在组件间共享
- SSR友好:支持服务端渲染
- 类型安全:完整的 TypeScript 支持
- 简单易用:无需额外配置
1.2 与其他方案对比 #
| 方案 | 复杂度 | 适用场景 |
|---|---|---|
| useState | 低 | 简单状态共享 |
| Pinia | 中 | 复杂状态管理 |
| Vuex | 高 | 大型应用(不推荐) |
二、基本用法 #
2.1 创建状态 #
vue
<script setup lang="ts">
const count = useState('count', () => 0)
</script>
<template>
<div>
<p>Count: {{ count }}</p>
<button @click="count++">增加</button>
</div>
</template>
2.2 唯一键 #
每个状态需要一个唯一的键:
vue
<script setup lang="ts">
const user = useState('user', () => null)
const cart = useState('cart', () => [])
const settings = useState('settings', () => ({
theme: 'light',
language: 'zh-CN'
}))
</script>
2.3 初始值函数 #
vue
<script setup lang="ts">
const items = useState('items', () => {
return Array.from({ length: 10 }, (_, i) => ({
id: i + 1,
name: `Item ${i + 1}`
}))
})
</script>
三、类型定义 #
3.1 基本类型 #
vue
<script setup lang="ts">
const count = useState<number>('count', () => 0)
const message = useState<string>('message', () => 'Hello')
const isActive = useState<boolean>('isActive', () => false)
</script>
3.2 对象类型 #
vue
<script setup lang="ts">
interface User {
id: number
name: string
email: string
}
const user = useState<User>('user', () => ({
id: 0,
name: '',
email: ''
}))
</script>
3.3 数组类型 #
vue
<script setup lang="ts">
interface CartItem {
id: number
name: string
price: number
quantity: number
}
const cart = useState<CartItem[]>('cart', () => [])
</script>
四、组合式函数封装 #
4.1 封装状态逻辑 #
composables/useCounter.ts:
typescript
export const useCounter = () => {
const count = useState('counter-count', () => 0)
const increment = () => count.value++
const decrement = () => count.value--
const reset = () => count.value = 0
return {
count,
increment,
decrement,
reset
}
}
使用:
vue
<script setup lang="ts">
const { count, increment, decrement, reset } = useCounter()
</script>
4.2 购物车状态 #
composables/useCart.ts:
typescript
interface CartItem {
id: number
name: string
price: number
quantity: number
}
export const useCart = () => {
const items = useState<CartItem[]>('cart-items', () => [])
const totalItems = computed(() =>
items.value.reduce((sum, item) => sum + item.quantity, 0)
)
const totalPrice = computed(() =>
items.value.reduce((sum, item) => sum + item.price * item.quantity, 0)
)
const addItem = (product: Omit<CartItem, 'quantity'>) => {
const existing = items.value.find(item => item.id === product.id)
if (existing) {
existing.quantity++
} else {
items.value.push({ ...product, quantity: 1 })
}
}
const removeItem = (id: number) => {
const index = items.value.findIndex(item => item.id === id)
if (index > -1) {
items.value.splice(index, 1)
}
}
const updateQuantity = (id: number, quantity: number) => {
const item = items.value.find(item => item.id === id)
if (item) {
item.quantity = Math.max(0, quantity)
if (item.quantity === 0) {
removeItem(id)
}
}
}
const clearCart = () => {
items.value = []
}
return {
items,
totalItems,
totalPrice,
addItem,
removeItem,
updateQuantity,
clearCart
}
}
4.3 用户认证状态 #
composables/useAuth.ts:
typescript
interface User {
id: number
name: string
email: string
role: string
}
export const useAuth = () => {
const user = useState<User | null>('auth-user', () => null)
const token = useCookie('auth-token')
const isAuthenticated = computed(() => !!user.value && !!token.value)
const isAdmin = computed(() => user.value?.role === 'admin')
const login = async (credentials: { email: string; password: string }) => {
const { data } = await useFetch('/api/auth/login', {
method: 'POST',
body: credentials
})
if (data.value) {
user.value = data.value.user
token.value = data.value.token
}
return data.value
}
const logout = async () => {
await useFetch('/api/auth/logout')
user.value = null
token.value = null
}
const fetchUser = async () => {
if (!token.value) return
const { data } = await useFetch('/api/auth/me')
user.value = data.value
}
return {
user,
token,
isAuthenticated,
isAdmin,
login,
logout,
fetchUser
}
}
五、SSR注意事项 #
5.1 服务端状态 #
vue
<script setup lang="ts">
const data = useState('server-data', () => {
if (import.meta.server) {
return 'Server initialized'
}
return 'Client initialized'
})
</script>
5.2 状态水合 #
vue
<script setup lang="ts">
const user = useState('user', () => null)
onMounted(async () => {
if (!user.value) {
const { data } = await useFetch('/api/auth/me')
user.value = data.value
}
})
</script>
5.3 避免内存泄漏 #
vue
<script setup lang="ts">
const route = useRoute()
const pageData = useState(`page-${route.path}`, () => null)
onUnmounted(() => {
clearNuxtState(`page-${route.path}`)
})
</script>
六、状态持久化 #
6.1 使用Cookie #
vue
<script setup lang="ts">
const theme = useCookie('theme', {
default: () => 'light',
watch: true
})
const toggleTheme = () => {
theme.value = theme.value === 'light' ? 'dark' : 'light'
}
</script>
6.2 使用localStorage #
composables/useLocalStorage.ts:
typescript
export const useLocalStorage = <T>(key: string, defaultValue: T) => {
const data = ref<T>(defaultValue)
if (import.meta.client) {
const stored = localStorage.getItem(key)
if (stored) {
data.value = JSON.parse(stored)
}
watch(data, (newValue) => {
localStorage.setItem(key, JSON.stringify(newValue))
}, { deep: true })
}
return data
}
使用:
vue
<script setup lang="ts">
const preferences = useLocalStorage('preferences', {
theme: 'light',
fontSize: 14
})
</script>
七、状态调试 #
7.1 开发模式日志 #
typescript
export const useDebugState = <T>(key: string, initialValue: T) => {
const state = useState(key, () => initialValue)
if (process.dev) {
watch(state, (newValue, oldValue) => {
console.log(`[${key}] State changed:`, { oldValue, newValue })
}, { deep: true })
}
return state
}
7.2 DevTools集成 #
Nuxt DevTools 可以查看 useState 的状态。
八、最佳实践 #
8.1 命名规范 #
typescript
const user = useState('auth-user')
const cart = useState('cart-items')
const settings = useState('app-settings')
8.2 封装组合式函数 #
typescript
export const useFeature = () => {
const state = useState('feature-state', () => ({
data: null,
loading: false,
error: null
}))
const actions = {
fetch: async () => {
state.value.loading = true
try {
const { data } = await useFetch('/api/feature')
state.value.data = data.value
} catch (error) {
state.value.error = error
} finally {
state.value.loading = false
}
}
}
return {
state: readonly(state),
...actions
}
}
8.3 避免过度使用 #
- 简单的父子通信使用 Props/Events
- 复杂的状态管理使用 Pinia
useState适合中等复杂度的状态共享
九、完整示例 #
9.1 主题切换 #
composables/useTheme.ts:
typescript
export const useTheme = () => {
const theme = useCookie<'light' | 'dark'>('theme', {
default: () => 'light',
watch: true
})
const isDark = computed(() => theme.value === 'dark')
const toggle = () => {
theme.value = isDark.value ? 'light' : 'dark'
}
const setTheme = (newTheme: 'light' | 'dark') => {
theme.value = newTheme
}
watchEffect(() => {
if (import.meta.client) {
document.documentElement.classList.toggle('dark', isDark.value)
}
})
return {
theme,
isDark,
toggle,
setTheme
}
}
9.2 全局通知 #
composables/useNotification.ts:
typescript
interface Notification {
id: number
type: 'success' | 'error' | 'warning' | 'info'
message: string
duration?: number
}
export const useNotification = () => {
const notifications = useState<Notification[]>('notifications', () => [])
let idCounter = 0
const add = (notification: Omit<Notification, 'id'>) => {
const id = ++idCounter
const newNotification: Notification = {
id,
duration: 5000,
...notification
}
notifications.value.push(newNotification)
if (newNotification.duration && newNotification.duration > 0) {
setTimeout(() => {
remove(id)
}, newNotification.duration)
}
return id
}
const remove = (id: number) => {
const index = notifications.value.findIndex(n => n.id === id)
if (index > -1) {
notifications.value.splice(index, 1)
}
}
const clear = () => {
notifications.value = []
}
const success = (message: string) => add({ type: 'success', message })
const error = (message: string) => add({ type: 'error', message })
const warning = (message: string) => add({ type: 'warning', message })
const info = (message: string) => add({ type: 'info', message })
return {
notifications,
add,
remove,
clear,
success,
error,
warning,
info
}
}
使用:
vue
<script setup lang="ts">
const { success, error } = useNotification()
const handleSave = async () => {
try {
await saveData()
success('保存成功!')
} catch (e) {
error('保存失败,请重试')
}
}
</script>
十、总结 #
本章介绍了 Nuxt.js 的 useState:
- 基本用法和类型定义
- 封装组合式函数
- SSR 注意事项
- 状态持久化
- 最佳实践
useState 适合简单到中等复杂度的状态管理,下一章我们将学习 Pinia 状态管理。
最后更新:2026-03-28