# 性能优化：Selector 与 Shallow

URL: https://caijiao.org/zustand/guide/performance-optimization
Source: docs/zustand/guide/performance-optimization.md
Description: 学习如何使用 Zustand 的 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 的优势

1. **减少不必要的重渲染**：只有当 Selector 选择的状态发生变化时，组件才会重渲染
2. **提高代码可读性**：明确指出组件依赖的状态
3. **便于维护**：当 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 功能来监控组件的重渲染情况，确保你的性能优化策略有效。

1. 打开 React DevTools
2. 切换到 Profiler 标签
3. 点击 "Record" 按钮
4. 与应用交互
5. 点击 "Stop" 按钮查看分析结果

## 总结

Selector 和 Shallow 是 Zustand 中强大的性能优化工具：

- **Selector** 允许组件精确订阅需要的状态，减少不必要的重渲染
- **Shallow** 提供了浅比较功能，优化了对象和数组的订阅
- 结合使用 Selector 和 Shallow，可以显著提高大型应用的性能

在接下来的章节中，我们将学习 [异步逻辑与 API 请求](async-actions.md)，了解如何在 Zustand 中处理异步操作。
