最佳实践 #

项目结构 #

小型项目结构 #

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