性能优化:Selector 与 Shallow
在构建大型应用时,性能优化是一个关键考虑因素。Zustand 提供了强大的性能优化工具,其中最重要的就是 Selector(选择器)和 Shallow(浅比较)。
Selector:精确订阅状态
Selector 是一种从 Store 中选择特定状态的函数。通过使用 Selector,组件可以只订阅它真正需要的状态,而不是整个 Store。
基本使用
jsx
// 只订阅 count 状态
const count = useStore((state) => state.count)
// 只订阅 name 状态
const name = useStore((state) => state.name)
// 订阅多个特定状态
const { count, name } = useStore((state) => ({
count: state.count,
name: state.name
}))Selector 的优势
- 减少不必要的重渲染:只有当 Selector 选择的状态发生变化时,组件才会重渲染
- 提高代码可读性:明确指出组件依赖的状态
- 便于维护:当 Store 结构变化时,只需要更新 Selector
Shallow:浅比较优化
默认情况下,当使用 Selector 返回一个新对象时,Zustand 会进行严格比较(===)。如果每次渲染都返回一个新对象,即使对象的属性值没有变化,也会触发组件重渲染。
浅比较的问题
jsx
// 不好的做法:每次渲染都返回一个新对象
const userInfo = useStore((state) => ({
name: state.user.name,
age: state.user.age
}))在上面的例子中,每次 Store 更新时,都会创建一个新的 userInfo 对象,即使 name 和 age 的值没有变化,也会触发组件重渲染。
使用 Shallow 进行优化
Zustand 提供了 shallow 选项,用于进行浅比较。当启用 shallow 时,Zustand 会比较对象的属性值,而不是对象本身。
jsx
import { useStore } from 'zustand'
import { shallow } from 'zustand/shallow'
// 好的做法:使用 shallow 进行浅比较
const userInfo = useStore(
(state) => ({
name: state.user.name,
age: state.user.age
}),
shallow // 启用浅比较
)现在,只有当 name 或 age 的值发生变化时,才会触发组件重渲染。
数组的浅比较
shallow 也适用于数组:
jsx
import { useStore } from 'zustand'
import { shallow } from 'zustand/shallow'
// 数组的浅比较
const completedTodos = useStore(
(state) => state.todos.filter((todo) => todo.completed),
shallow
)选择器的高级用法
组合选择器
你可以组合多个选择器来创建更复杂的选择逻辑:
javascript
// 创建可复用的选择器
const selectCount = (state) => state.count
const selectName = (state) => state.name
// 在组件中使用
const count = useStore(selectCount)
const name = useStore(selectName)带参数的选择器
你可以创建带参数的选择器:
javascript
// 创建带参数的选择器
const selectTodoById = (id) => (state) =>
state.todos.find((todo) => todo.id === id)
// 在组件中使用
function TodoItem({ id }) {
const todo = useStore(selectTodoById(id))
// ...
}性能优化的最佳实践
1. 总是使用 Selector
避免直接使用整个 Store:
jsx
// 不好的做法:订阅整个 Store
const store = useStore()
const count = store.count
// 好的做法:使用 Selector
const count = useStore((state) => state.count)2. 对于复杂对象使用 Shallow
当 Selector 返回对象或数组时,使用 shallow 进行优化:
jsx
import { shallow } from 'zustand/shallow'
// 好的做法:使用 shallow
const userInfo = useStore(
(state) => ({
name: state.user.name,
age: state.user.age
}),
shallow
)3. 缓存计算结果
对于需要计算的选择器,可以使用记忆化(memoization)来避免重复计算:
javascript
import { createSelector } from 'reselect'
// 创建记忆化的选择器
const selectCompletedTodosCount = createSelector(
[(state) => state.todos],
(todos) => todos.filter((todo) => todo.completed).length
)
// 在组件中使用
const completedCount = useStore(selectCompletedTodosCount)4. 避免在 Selector 中创建新对象
除非使用 shallow,否则避免在 Selector 中创建新对象:
jsx
// 不好的做法:创建新对象且不使用 shallow
const userInfo = useStore((state) => ({
name: state.user.name,
age: state.user.age
}))
// 好的做法 1:使用 shallow
const userInfo = useStore(
(state) => ({
name: state.user.name,
age: state.user.age
}),
shallow
)
// 好的做法 2:分别选择属性
const name = useStore((state) => state.user.name)
const age = useStore((state) => state.user.age)性能监控
你可以使用 React DevTools 的 Profiler 功能来监控组件的重渲染情况,确保你的性能优化策略有效。
- 打开 React DevTools
- 切换到 Profiler 标签
- 点击 "Record" 按钮
- 与应用交互
- 点击 "Stop" 按钮查看分析结果
总结
Selector 和 Shallow 是 Zustand 中强大的性能优化工具:
- Selector 允许组件精确订阅需要的状态,减少不必要的重渲染
- Shallow 提供了浅比较功能,优化了对象和数组的订阅
- 结合使用 Selector 和 Shallow,可以显著提高大型应用的性能
在接下来的章节中,我们将学习 异步逻辑与 API 请求,了解如何在 Zustand 中处理异步操作。