性能优化 #
理解 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