最佳实践 #
项目结构 #
小型项目结构 #
text
src/
├── components/
│ └── Counter.tsx
├── stores/
│ └── counterStore.ts
├── App.tsx
└── main.tsx
中型项目结构 #
text
src/
├── components/
│ ├── common/
│ │ ├── Button.tsx
│ │ └── Input.tsx
│ └── features/
│ ├── counter/
│ │ ├── Counter.tsx
│ │ └── CounterControls.tsx
│ └── user/
│ ├── UserProfile.tsx
│ └── UserSettings.tsx
├── stores/
│ ├── index.ts
│ ├── counterStore.ts
│ └── userStore.ts
├── hooks/
│ ├── useCounter.ts
│ └── useUser.ts
├── types/
│ └── index.ts
├── utils/
│ └── storage.ts
├── App.tsx
└── main.tsx
大型项目结构 #
text
src/
├── features/
│ ├── auth/
│ │ ├── components/
│ │ │ ├── LoginForm.tsx
│ │ │ └── RegisterForm.tsx
│ │ ├── stores/
│ │ │ ├── authSlice.ts
│ │ │ └── sessionSlice.ts
│ │ ├── hooks/
│ │ │ ├── useAuth.ts
│ │ │ └── useSession.ts
│ │ ├── types/
│ │ │ └── index.ts
│ │ └── index.ts
│ ├── cart/
│ │ ├── components/
│ │ ├── stores/
│ │ ├── hooks/
│ │ ├── types/
│ │ └── index.ts
│ └── products/
│ ├── components/
│ ├── stores/
│ ├── hooks/
│ ├── types/
│ └── index.ts
├── stores/
│ ├── index.ts
│ └── rootStore.ts
├── shared/
│ ├── components/
│ ├── hooks/
│ ├── utils/
│ └── types/
├── App.tsx
└── main.tsx
命名规范 #
Store 命名 #
tsx
// ✅ 好:使用 useXxxStore 命名
const useUserStore = create(...)
const useCartStore = create(...)
const useThemeStore = create(...)
// ❌ 不好:不清晰的命名
const useStore = create(...)
const store = create(...)
const userState = create(...)
文件命名 #
text
// ✅ 好:使用 camelCase
stores/userStore.ts
stores/cartStore.ts
stores/themeStore.ts
// ❌ 不好:不一致的命名
stores/UserStore.ts
stores/cart-store.ts
stores/Theme.ts
状态和 Action 命名 #
tsx
// ✅ 好:清晰的状态命名
interface UserState {
user: User | null
isAuthenticated: boolean
isLoading: boolean
error: string | null
}
// ✅ 好:动词开头的 Action 命名
interface UserActions {
fetchUser: (id: string) => Promise<void>
updateUser: (data: Partial<User>) => void
clearUser: () => void
setLoading: (loading: boolean) => void
}
// ❌ 不好:不清晰的命名
interface State {
data: any
flag: boolean
temp: string
}
TypeScript 最佳实践 #
完整类型定义 #
tsx
// ✅ 好:完整的类型定义
interface User {
id: string
name: string
email: string
role: 'admin' | 'user' | 'guest'
}
interface UserState {
user: User | null
isAuthenticated: boolean
isLoading: boolean
error: string | null
// Actions
login: (email: string, password: string) => Promise<void>
logout: () => void
updateUser: (data: Partial<User>) => void
}
const useUserStore = create<UserState>((set) => ({
// ...
}))
类型导出 #
tsx
// types/user.ts
export interface User {
id: string
name: string
email: string
}
// stores/userStore.ts
import type { User } from '../types/user'
export interface UserState {
user: User | null
// ...
}
泛型工具 #
tsx
// types/store.ts
export type Action<T extends (...args: any[]) => any> = T
export type Actions<T extends object> = {
[K in keyof T]: T[K] extends (...args: any[]) => any ? T[K] : never
}
export type State<T extends object> = {
[K in keyof T]: T[K] extends (...args: any[]) => any ? never : T[K]
}
状态设计原则 #
单一职责 #
tsx
// ✅ 好:每个 store 负责一个领域
const useUserStore = create(...)
const useCartStore = create(...)
const useThemeStore = create(...)
// ❌ 不好:一个 store 管理所有
const useAppStore = create((set) => ({
user: {},
cart: {},
theme: {},
products: {},
// ...太多职责
}))
最小状态 #
tsx
// ✅ 好:只存储必要的状态
interface CartState {
items: CartItem[]
}
// ❌ 不好:存储可计算的状态
interface CartState {
items: CartItem[]
totalItems: number // 可从 items 计算
totalPrice: number // 可从 items 计算
}
// ✅ 好:使用计算属性
const useCartStore = create<CartState>((set, get) => ({
items: [],
getTotalItems: () => get().items.reduce((sum, item) => sum + item.quantity, 0),
getTotalPrice: () => get().items.reduce((sum, item) => sum + item.price * item.quantity, 0),
}))
规范化状态 #
tsx
// ❌ 不好:嵌套结构
interface State {
posts: {
id: string
title: string
comments: {
id: string
text: string
user: {
id: string
name: string
}
}[]
}[]
}
// ✅ 好:规范化结构
interface State {
posts: Record<string, Post>
comments: Record<string, Comment>
users: Record<string, User>
postComments: Record<string, string[]> // postId -> commentIds
}
性能最佳实践 #
精确订阅 #
tsx
// ❌ 不好:订阅整个 store
const store = useStore()
// ✅ 好:只订阅需要的状态
const count = useStore((state) => state.count)
使用 shallow #
tsx
import { shallow } from 'zustand/shallow'
// ✅ 好:使用 shallow 比较对象
const { count, name } = useStore(
(state) => ({ count: state.count, name: state.name }),
shallow
)
批量更新 #
tsx
// ❌ 不好:多次更新
setName('John')
setEmail('john@example.com')
setAge(25)
// ✅ 好:一次更新
updateUser({
name: 'John',
email: 'john@example.com',
age: 25,
})
组件拆分 #
tsx
// ❌ 不好:大组件订阅多个状态
function Dashboard() {
const user = useStore((state) => state.user)
const posts = useStore((state) => state.posts)
const notifications = useStore((state) => state.notifications)
// ...
}
// ✅ 好:拆分组件
function Dashboard() {
return (
<>
<UserProfile />
<PostList />
<NotificationList />
</>
)
}
function UserProfile() {
const user = useStore((state) => state.user)
// ...
}
错误处理 #
统一错误处理 #
tsx
interface State {
error: string | null
clearError: () => void
}
const useStore = create<State>((set) => ({
error: null,
clearError: () => set({ error: null }),
}))
// 组件中使用
function ErrorMessage() {
const error = useStore((state) => state.error)
const clearError = useStore((state) => state.clearError)
if (!error) return null
return (
<div className="error">
{error}
<button onClick={clearError}>关闭</button>
</div>
)
}
异步错误处理 #
tsx
interface State {
isLoading: boolean
error: string | null
fetchData: () => Promise<void>
}
const useStore = create<State>((set) => ({
isLoading: false,
error: null,
fetchData: async () => {
set({ isLoading: true, error: null })
try {
const response = await fetch('/api/data')
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`)
}
const data = await response.json()
set({ data, isLoading: false })
} catch (error) {
set({
error: error instanceof Error ? error.message : '未知错误',
isLoading: false,
})
}
},
}))
测试最佳实践 #
隔离测试 #
tsx
import { describe, it, expect, beforeEach } from 'vitest'
describe('UserStore', () => {
beforeEach(() => {
// 重置状态
useUserStore.setState({
user: null,
isAuthenticated: false,
isLoading: false,
error: null,
})
})
it('should login successfully', async () => {
// ...
})
})
Mock 外部依赖 #
tsx
import { vi } from 'vitest'
global.fetch = vi.fn()
describe('UserStore', () => {
beforeEach(() => {
vi.clearAllMocks()
})
it('should fetch user', async () => {
;(fetch as any).mockResolvedValueOnce({
ok: true,
json: () => Promise.resolve({ id: '1', name: 'John' }),
})
await useUserStore.getState().fetchUser('1')
expect(useUserStore.getState().user).toEqual({ id: '1', name: 'John' })
})
})
常见陷阱 #
陷阱1:直接修改状态 #
tsx
// ❌ 不好:直接修改
const state = useStore.getState()
state.count = 10
// ✅ 好:使用 set
useStore.setState({ count: 10 })
陷阱2:在渲染中调用 setState #
tsx
// ❌ 不好:在渲染中调用
function Component() {
useStore.getState().fetchData() // 无限循环
return <div>...</div>
}
// ✅ 好:在 useEffect 中调用
function Component() {
useEffect(() => {
useStore.getState().fetchData()
}, [])
return <div>...</div>
}
陷阱3:忘记类型断言 #
tsx
// ❌ 不好:中间件类型推断失败
const useStore = create(
persist((set) => ({ count: 0 }), { name: 'store' })
)
// ✅ 好:使用类型断言
const useStore = create<State>()(
persist((set) => ({ count: 0 }), { name: 'store' })
)
陷阱4:过度使用 Store #
tsx
// ❌ 不好:所有状态都放在 store
const useStore = create((set) => ({
inputValue: '',
isModalOpen: false,
selectedTab: 0,
// ...这些可以是组件本地状态
}))
// ✅ 好:区分全局和本地状态
function Component() {
// 本地状态
const [inputValue, setInputValue] = useState('')
const [isModalOpen, setIsModalOpen] = useState(false)
// 全局状态
const user = useUserStore((state) => state.user)
}
安全最佳实践 #
不要存储敏感信息 #
tsx
// ❌ 不好:存储敏感信息
const useAuthStore = create(
persist(
(set) => ({
password: '',
token: '',
creditCard: '',
}),
{ name: 'auth' }
)
)
// ✅ 好:排除敏感信息
const useAuthStore = create(
persist(
(set) => ({
user: null,
token: null,
}),
{
name: 'auth',
partialize: (state) => ({
user: state.user,
// 不持久化 token
}),
}
)
)
输入验证 #
tsx
const useFormStore = create((set, get) => ({
email: '',
setEmail: (email: string) => {
// 验证输入
if (!email.includes('@')) {
throw new Error('Invalid email')
}
set({ email })
},
}))
文档和注释 #
类型注释 #
tsx
interface UserState {
/**
* 当前登录用户
* 未登录时为 null
*/
user: User | null
/**
* 是否已认证
*/
isAuthenticated: boolean
/**
* 用户登录
* @param email - 用户邮箱
* @param password - 用户密码
* @throws {Error} 登录失败时抛出错误
*/
login: (email: string, password: string) => Promise<void>
}
README 文档 #
markdown
# Store 使用指南
## 用户状态 (useUserStore)
### 状态
| 属性 | 类型 | 说明 |
|------|------|------|
| user | User \| null | 当前用户 |
| isAuthenticated | boolean | 是否已认证 |
### Actions
| 方法 | 参数 | 说明 |
|------|------|------|
| login | email, password | 用户登录 |
| logout | - | 用户登出 |
## 使用示例
\`\`\`tsx
function LoginPage() {
const { login, isLoading, error } = useUserStore()
const handleLogin = async (email, password) => {
await login(email, password)
}
// ...
}
\`\`\`
检查清单 #
text
✅ Zustand 最佳实践检查清单
├── 项目结构
│ ├── 按功能模块组织
│ ├── 分离类型定义
│ └── 统一导出入口
├── 命名规范
│ ├── Store 使用 useXxxStore
│ ├── Actions 使用动词开头
│ └── 文件使用 camelCase
├── 类型安全
│ ├── 完整的接口定义
│ ├── 中间件类型断言
│ └── 导出类型供使用
├── 性能优化
│ ├── 精确选择器
│ ├── 使用 shallow 比较
│ ├── 批量更新
│ └── 组件拆分
├── 错误处理
│ ├── 统一错误状态
│ ├── 异步错误捕获
│ └── 用户友好提示
└── 安全考虑
├── 不存储敏感信息
├── 输入验证
└── XSS 防护
总结 #
遵循最佳实践可以:
- 提高代码可维护性
- 减少错误和 bug
- 提升应用性能
- 便于团队协作
- 确保代码质量
恭喜你完成了 Zustand 完全指南的学习!现在你已经掌握了从基础到高级的所有知识,可以自信地在项目中使用 Zustand 进行状态管理了。
最后更新:2026-03-28