Observer观察者 #
observer 是 mobx-react-lite 提供的高阶组件,它让 React 组件能够自动响应 MobX 状态的变化。当组件中使用的可观察状态发生变化时,组件会自动重新渲染。
基本用法 #
简单示例 #
javascript
import { makeAutoObservable } from 'mobx';
import { observer } from 'mobx-react-lite';
// 创建 Store
class CounterStore {
count = 0;
constructor() {
makeAutoObservable(this);
}
increment() {
this.count++;
}
}
const counterStore = new CounterStore();
// 使用 observer 包装组件
const Counter = observer(() => {
return (
<div>
<p>Count: {counterStore.count}</p>
<button onClick={() => counterStore.increment()}>+1</button>
</div>
);
});
export default Counter;
工作原理 #
text
┌─────────────────────────────────────────────────────────────┐
│ observer 工作原理 │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. 组件渲染时,observer 追踪所有使用的可观察状态 │
│ │
│ 2. 状态变化时,observer 触发组件重新渲染 │
│ │
│ 3. 只有实际使用的状态变化才会触发更新 │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Store │ ──► │ observer │ ──► │ Component │ │
│ │ (状态) │ │ (追踪) │ │ (渲染) │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │ │ │
│ └────────────────┘ │
│ 状态变化触发更新 │
│ │
└─────────────────────────────────────────────────────────────┘
observer的特点 #
1. 细粒度更新 #
只有组件实际使用的状态变化才会触发更新:
javascript
const UserCard = observer(({ user }) => {
// 只追踪 user.name
return <div>{user.name}</div>;
});
// 修改 name 会触发更新
user.name = 'New Name';
// 修改 age 不会触发更新(因为组件没有使用 age)
user.age = 30;
2. 自动追踪 #
自动追踪渲染过程中访问的所有可观察状态:
javascript
const TodoList = observer(() => {
return (
<div>
<p>Total: {todoStore.totalCount}</p>
<p>Active: {todoStore.activeCount}</p>
<ul>
{todoStore.filteredTodos.map(todo => (
<li key={todo.id}>{todo.title}</li>
))}
</ul>
</div>
);
});
// 自动追踪: totalCount, activeCount, filteredTodos
3. 批量更新 #
多个状态修改只触发一次渲染:
javascript
const store = makeAutoObservable({
a: 0,
b: 0
});
const Component = observer(() => {
return <div>{store.a} - {store.b}</div>;
});
// 批量修改,只渲染一次
store.a = 1;
store.b = 2;
使用方式 #
函数组件(推荐) #
javascript
import { observer } from 'mobx-react-lite';
const Counter = observer(() => {
return <div>{counterStore.count}</div>;
});
带Props的组件 #
javascript
const TodoItem = observer(({ todo }) => {
return (
<div>
<span>{todo.title}</span>
<button onClick={() => todo.toggle()}>Toggle</button>
</div>
);
});
类组件 #
javascript
import { observer } from 'mobx-react';
class Counter extends React.Component {
render() {
return <div>{counterStore.count}</div>;
}
}
export default observer(Counter);
完整示例 #
Todo应用 #
javascript
import { makeAutoObservable } from 'mobx';
import { observer } from 'mobx-react-lite';
// Store
class TodoStore {
todos = [];
filter = 'all';
constructor() {
makeAutoObservable(this);
}
addTodo(title) {
this.todos.push({
id: Date.now(),
title,
completed: false
});
}
toggleTodo(id) {
const todo = this.todos.find(t => t.id === id);
if (todo) todo.completed = !todo.completed;
}
setFilter(filter) {
this.filter = filter;
}
get filteredTodos() {
switch (this.filter) {
case 'completed':
return this.todos.filter(t => t.completed);
case 'active':
return this.todos.filter(t => !t.completed);
default:
return this.todos;
}
}
get activeCount() {
return this.todos.filter(t => !t.completed).length;
}
}
const todoStore = new TodoStore();
// 组件
const TodoItem = observer(({ todo }) => (
<li
style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}
onClick={() => todoStore.toggleTodo(todo.id)}
>
{todo.title}
</li>
));
const TodoList = observer(() => (
<ul>
{todoStore.filteredTodos.map(todo => (
<TodoItem key={todo.id} todo={todo} />
))}
</ul>
));
const FilterButtons = observer(() => (
<div>
<button onClick={() => todoStore.setFilter('all')}>All</button>
<button onClick={() => todoStore.setFilter('active')}>Active</button>
<button onClick={() => todoStore.setFilter('completed')}>Completed</button>
</div>
));
const AddTodo = () => {
const [title, setTitle] = React.useState('');
const handleSubmit = (e) => {
e.preventDefault();
if (title.trim()) {
todoStore.addTodo(title);
setTitle('');
}
};
return (
<form onSubmit={handleSubmit}>
<input
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="Add todo"
/>
<button type="submit">Add</button>
</form>
);
};
const TodoApp = observer(() => (
<div>
<h1>Todo ({todoStore.activeCount} remaining)</h1>
<AddTodo />
<TodoList />
<FilterButtons />
</div>
));
export default TodoApp;
用户列表 #
javascript
import { makeAutoObservable, runInAction } from 'mobx';
import { observer } from 'mobx-react-lite';
class UserStore {
users = [];
loading = false;
error = null;
constructor() {
makeAutoObservable(this);
}
async fetchUsers() {
this.loading = true;
try {
const response = await fetch('/api/users');
const users = await response.json();
runInAction(() => {
this.users = users;
this.error = null;
});
} catch (error) {
runInAction(() => {
this.error = error.message;
});
} finally {
runInAction(() => {
this.loading = false;
});
}
}
}
const userStore = new UserStore();
const UserList = observer(() => {
if (userStore.loading) return <div>Loading...</div>;
if (userStore.error) return <div>Error: {userStore.error}</div>;
return (
<ul>
{userStore.users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
});
const UserApp = () => {
React.useEffect(() => {
userStore.fetchUsers();
}, []);
return (
<div>
<h1>Users</h1>
<UserList />
</div>
);
};
export default UserApp;
性能优化 #
1. 组件拆分 #
javascript
// 不好:一个大组件
const BigComponent = observer(() => {
return (
<div>
<Header user={userStore.user} />
<Content items={itemStore.items} />
<Footer count={counterStore.count} />
</div>
);
});
// 好:拆分成小组件
const Header = observer(() => (
<header>{userStore.user.name}</header>
));
const Content = observer(() => (
<main>{itemStore.items.map(...)}</main>
));
const Footer = observer(() => (
<footer>{counterStore.count}</footer>
));
const BigComponent = () => (
<div>
<Header />
<Content />
<Footer />
</div>
);
2. 避免不必要的追踪 #
javascript
// 不好:追踪整个对象
const UserCard = observer(({ user }) => (
<div>{JSON.stringify(user)}</div>
));
// 好:只追踪需要的属性
const UserCard = observer(({ user }) => (
<div>{user.name}</div>
));
3. 使用useMemo #
javascript
const ExpensiveList = observer(() => {
const sortedItems = React.useMemo(
() => [...itemStore.items].sort((a, b) => a.name.localeCompare(b.name)),
[itemStore.items]
);
return (
<ul>
{sortedItems.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
);
});
注意事项 #
1. 不要在observer外访问状态 #
javascript
// 不好:在 observer 外访问状态
const count = counterStore.count;
const Counter = observer(() => {
return <div>{count}</div>; // 不会响应变化
});
// 好:在 observer 内访问状态
const Counter = observer(() => {
return <div>{counterStore.count}</div>;
});
2. 解构会失去响应性 #
javascript
// 不好:解构后失去响应性
const Counter = observer(() => {
const { count } = counterStore; // 失去响应性
return <div>{count}</div>;
});
// 好:直接访问
const Counter = observer(() => {
return <div>{counterStore.count}</div>;
});
3. 异步渲染中的状态访问 #
javascript
// 不好:异步访问状态
const BadComponent = observer(() => {
const [data, setData] = React.useState(null);
React.useEffect(() => {
setTimeout(() => {
setData(store.data); // 不在渲染中,不会被追踪
}, 1000);
}, []);
return <div>{data}</div>;
});
// 好:在渲染中访问状态
const GoodComponent = observer(() => {
return <div>{store.data}</div>;
});
总结 #
observer 的核心要点:
- 自动追踪:自动追踪渲染中使用的状态
- 细粒度更新:只有使用的状态变化才更新
- 批量更新:多个修改只触发一次渲染
- 高性能:避免不必要的渲染
使用建议:
- 所有使用 MobX 状态的组件都用 observer 包装
- 组件拆分,提高更新精度
- 在 observer 内访问状态
- 避免解构可观察对象
继续学习 Observer组件,了解更细粒度的响应式控制。
最后更新:2026-03-28