性能优化 #

理解 Zustand 的性能特点 #

Zustand 本身已经非常轻量和高效,但在大型应用中,合理使用可以进一步提升性能。

text
Zustand 性能优势
├── 精确订阅 ──── 只更新订阅的组件
├── 无 Provider ── 减少组件树层级
├── 轻量级 ────── ~1KB gzipped
└── 简单更新 ──── 无需 action dispatch

选择器优化 #

问题:过度订阅 #

tsx
// ❌ 不好:订阅整个 store
function Component() {
  const store = useStore()
  return <div>{store.count}</div>
}

当 store 中任何状态变化时,组件都会重渲染,即使它只使用 count

解决方案:精确选择器 #

tsx
// ✅ 好:只订阅需要的状态
function Component() {
  const count = useStore((state) => state.count)
  return <div>{count}</div>
}

选择器返回对象的问题 #

tsx
// ⚠️ 问题:每次渲染都创建新对象
function Component() {
  const { count, name } = useStore((state) => ({
    count: state.count,
    name: state.name,
  }))
  return <div>{count} - {name}</div>
}

每次渲染,选择器都返回一个新对象,导致组件重渲染。

解决方案:使用 shallow #

tsx
import { shallow } from 'zustand/shallow'

// ✅ 好:使用 shallow 比较
function Component() {
  const { count, name } = useStore(
    (state) => ({ count: state.count, name: state.name }),
    shallow
  )
  return <div>{count} - {name}</div>
}

使用 useShallow Hook #

tsx
import { useShallow } from 'zustand/react/shallow'

function Component() {
  const { count, name } = useStore(
    useShallow((state) => ({
      count: state.count,
      name: state.name,
    }))
  )
  return <div>{count} - {name}</div>
}

自定义比较函数 #

基本用法 #

tsx
function Component() {
  const items = useStore(
    (state) => state.items,
    (prev, next) => {
      // 只比较长度
      return prev.length === next.length
    }
  )
  return <div>{items.length} items</div>
}

深度比较 #

tsx
import { isEqual } from 'lodash-es'

function Component() {
  const user = useStore(
    (state) => state.user,
    isEqual  // 深度比较
  )
  return <div>{user.name}</div>
}

选择性深度比较 #

tsx
function Component() {
  const todos = useStore(
    (state) => state.todos,
    (prev, next) => {
      // 只比较 id 和 completed
      if (prev.length !== next.length) return false
      return prev.every((p, i) => 
        p.id === next[i].id && p.completed === next[i].completed
      )
    }
  )
  return <TodoList todos={todos} />
}

避免不必要的重渲染 #

问题:内联选择器 #

tsx
// ❌ 不好:内联计算
function Component() {
  const doubleCount = useStore((state) => state.count * 2)
  return <div>{doubleCount}</div>
}

每次 count 变化都会触发重渲染,即使结果相同。

解决方案:记忆化选择器 #

tsx
import { useMemo } from 'react'

function Component() {
  const count = useStore((state) => state.count)
  const doubleCount = useMemo(() => count * 2, [count])
  return <div>{doubleCount}</div>
}

使用稳定的引用 #

tsx
// ❌ 不好:每次创建新数组
function Component() {
  const items = useStore((state) => state.items.filter(i => i.active))
  return <List items={items} />
}

// ✅ 好:在 store 中计算
const useStore = create((set, get) => ({
  items: [],
  activeItems: () => get().items.filter(i => i.active),
}))

function Component() {
  const activeItems = useStore((state) => state.activeItems())
  return <List items={activeItems} />
}

批量更新 #

问题:连续更新 #

tsx
// ❌ 不好:多次更新
function Component() {
  const { setName, setEmail, setAge } = useStore((state) => state)
  
  const handleUpdate = () => {
    setName('John')
    setEmail('john@example.com')
    setAge(25)
  }
  
  return <button onClick={handleUpdate}>Update</button>
}

每次 set 都会触发一次重渲染。

解决方案:批量更新 #

tsx
// ✅ 好:一次更新
function Component() {
  const updateUser = useStore((state) => state.updateUser)
  
  const handleUpdate = () => {
    updateUser({
      name: 'John',
      email: 'john@example.com',
      age: 25,
    })
  }
  
  return <button onClick={handleUpdate}>Update</button>
}

// Store 定义
const useStore = create((set) => ({
  user: { name: '', email: '', age: 0 },
  updateUser: (data) => set((state) => ({
    user: { ...state.user, ...data }
  })),
}))

组件拆分 #

问题:大组件订阅过多 #

tsx
// ❌ 不好:一个组件订阅多个状态
function UserDashboard() {
  const user = useStore((state) => state.user)
  const posts = useStore((state) => state.posts)
  const comments = useStore((state) => state.comments)
  const notifications = useStore((state) => state.notifications)
  
  return (
    <div>
      <UserProfile user={user} />
      <PostList posts={posts} />
      <CommentList comments={comments} />
      <NotificationList notifications={notifications} />
    </div>
  )
}

解决方案:组件拆分 #

tsx
// ✅ 好:每个组件订阅自己的状态
function UserDashboard() {
  return (
    <div>
      <UserProfile />
      <PostList />
      <CommentList />
      <NotificationList />
    </div>
  )
}

function UserProfile() {
  const user = useStore((state) => state.user)
  return <div>{user.name}</div>
}

function PostList() {
  const posts = useStore((state) => state.posts)
  return <ul>{posts.map(p => <li key={p.id}>{p.title}</li>)}</ul>
}

懒加载 Store #

动态导入 Store #

tsx
// stores/index.ts
export const useUserStore = create((set) => ({
  user: null,
}))

// 懒加载
let useLazyStore: any

function useLazyStoreHook() {
  const [loaded, setLoaded] = useState(false)
  
  useEffect(() => {
    import('./lazyStore').then((module) => {
      useLazyStore = module.useLazyStore
      setLoaded(true)
    })
  }, [])
  
  return loaded ? useLazyStore : null
}

条件加载 #

tsx
function FeatureComponent() {
  const [showFeature, setShowFeature] = useState(false)
  
  return (
    <div>
      <button onClick={() => setShowFeature(true)}>Show Feature</button>
      {showFeature && <LazyFeature />}
    </div>
  )
}

function LazyFeature() {
  const store = useFeatureStore()  // 只在这里加载
  return <div>{store.data}</div>
}

性能监控 #

渲染计数 #

tsx
function useRenderCount(name: string) {
  const count = useRef(0)
  count.current++
  
  useEffect(() => {
    console.log(`[${name}] Render #${count.current}`)
  })
}

function Component() {
  useRenderCount('MyComponent')
  const count = useStore((state) => state.count)
  return <div>{count}</div>
}

选择器性能监控 #

tsx
function useMonitoredSelector<T>(
  selector: (state: any) => T,
  name: string
): T {
  const startTime = performance.now()
  const result = useStore(selector)
  const endTime = performance.now()
  
  useEffect(() => {
    if (endTime - startTime > 1) {
      console.warn(`[${name}] Slow selector: ${endTime - startTime}ms`)
    }
  }, [endTime, startTime, name])
  
  return result
}

更新频率监控 #

tsx
const createMonitoredStore = (name: string) => {
  let updateCount = 0
  let lastLog = Date.now()
  
  return create((set, get, api) => ({
    ...createStore(set, get, api),
    
    set: (args: any) => {
      updateCount++
      const now = Date.now()
      
      if (now - lastLog >= 1000) {
        console.log(`[${name}] Updates/sec: ${updateCount}`)
        updateCount = 0
        lastLog = now
      }
      
      set(args)
    },
  }))
}

实际案例 #

优化前 #

tsx
function TodoApp() {
  const { todos, filter, addTodo, toggleTodo, removeTodo, setFilter } = useStore()
  
  const filteredTodos = useMemo(() => {
    switch (filter) {
      case 'active':
        return todos.filter((t) => !t.completed)
      case 'completed':
        return todos.filter((t) => t.completed)
      default:
        return todos
    }
  }, [todos, filter])
  
  return (
    <div>
      <TodoInput onAdd={addTodo} />
      <FilterButtons filter={filter} onFilterChange={setFilter} />
      <TodoList todos={filteredTodos} onToggle={toggleTodo} onRemove={removeTodo} />
    </div>
  )
}

优化后 #

tsx
import { shallow } from 'zustand/shallow'

// 拆分组件
function TodoApp() {
  return (
    <div>
      <TodoInput />
      <FilterButtons />
      <TodoList />
    </div>
  )
}

function TodoInput() {
  const addTodo = useStore((state) => state.addTodo)
  const [text, setText] = useState('')
  
  const handleAdd = () => {
    if (text.trim()) {
      addTodo(text)
      setText('')
    }
  }
  
  return (
    <div>
      <input value={text} onChange={(e) => setText(e.target.value)} />
      <button onClick={handleAdd}>Add</button>
    </div>
  )
}

function FilterButtons() {
  const { filter, setFilter } = useStore(
    (state) => ({ filter: state.filter, setFilter: state.setFilter }),
    shallow
  )
  
  return (
    <div>
      <button onClick={() => setFilter('all')}>All</button>
      <button onClick={() => setFilter('active')}>Active</button>
      <button onClick={() => setFilter('completed')}>Completed</button>
    </div>
  )
}

function TodoList() {
  const filter = useStore((state) => state.filter)
  const todos = useStore(
    (state) => {
      switch (filter) {
        case 'active':
          return state.todos.filter((t) => !t.completed)
        case 'completed':
          return state.todos.filter((t) => t.completed)
        default:
          return state.todos
      }
    },
    (prev, next) => {
      if (prev.length !== next.length) return false
      return prev.every((p, i) => 
        p.id === next[i].id && p.completed === next[i].completed
      )
    }
  )
  
  return (
    <ul>
      {todos.map((todo) => (
        <TodoItem key={todo.id} todo={todo} />
      ))}
    </ul>
  )
}

function TodoItem({ todo }: { todo: Todo }) {
  const { toggleTodo, removeTodo } = useStore(
    (state) => ({
      toggleTodo: state.toggleTodo,
      removeTodo: state.removeTodo,
    }),
    shallow
  )
  
  return (
    <li>
      <input
        type="checkbox"
        checked={todo.completed}
        onChange={() => toggleTodo(todo.id)}
      />
      <span>{todo.text}</span>
      <button onClick={() => removeTodo(todo.id)}>Delete</button>
    </li>
  )
}

性能检查清单 #

text
✅ 性能优化检查清单
├── 选择器
│   ├── 只选择需要的状态
│   ├── 使用 shallow 比较对象
│   └── 避免内联复杂计算
├── 组件
│   ├── 拆分大组件
│   ├── 使用 memo 包装子组件
│   └── 避免不必要的 props
├── 更新
│   ├── 批量更新状态
│   ├── 避免连续 set 调用
│   └── 使用 immer 处理复杂更新
└── 监控
    ├── 检查渲染次数
    ├── 监控更新频率
    └── 使用 React DevTools

总结 #

性能优化的关键点:

  • 使用精确的选择器订阅状态
  • 使用 shallow 比较对象选择器
  • 批量更新减少重渲染
  • 拆分组件降低订阅范围
  • 监控性能找出瓶颈

接下来,让我们学习 Store 切片模式,掌握模块化状态管理。

最后更新:2026-03-28