一、MobX 的底层原理(简版)
MobX 的核心是通过 依赖追踪 和 发布-订阅模式 实现的响应式系统:
-
可观察状态(Observable) 将数据转换为可观察对象。
-
依赖收集(Tracking) 在组件渲染或计算过程中,自动记录依赖的可观察属性。
-
触发更新(Reaction) 当依赖的状态变化时,自动触发相关组件或计算逻辑的更新。
这种机制比基于手动依赖声明的 useMemo
或 useEffect
更精准和高效。
二、MobX 与 React 函数式组件深度整合
1. mobx
vs mobx-react-lite
-
mobx
:MobX 核心库,提供状态管理的基础能力(observable
,action
,computed
,reaction
,autorun
,toJS
,runInAction
,makeAutoObservable
等)。 -
mobx-react-lite
:专为 React 函数式组件设计的轻量级绑定库,提供observer
,useObserver
,useLocalStore
(已废弃)等 API。特点:-
仅支持函数组件(类组件需用
mobx-react
)。 -
更小的体积,更好的性能优化。
-
2. 核心 API 在函数式组件中的应用
(1) observer
(组件响应式绑定)
将组件包裹为“观察者”,自动追踪依赖的可观察状态,并在状态变化时重新渲染组件。
import { observer } from "mobx-react-lite";
import { store } from "./store";
const UserList = observer(() => {
return (
<div>
{store.users.map(user => (
<div key={user.id}>{user.name}</div>
))}
</div>
);
});
(2) useLocalStore
(已废弃 → 改用 useMemo
+ makeAutoObservable
)
旧版用于在组件内创建局部可观察状态的 Hook(现推荐直接使用 useMemo
)。
import { observer, useLocalStore } from "mobx-react-lite";
const Counter = observer(() => {
// 旧版写法(已废弃)
const store = useLocalStore(() => ({
count: 0,
increment() {
store.count++;
},
}));
// 推荐新写法
const store = useMemo(() => makeAutoObservable({
count: 0,
increment() {
this.count++;
},
}), []);
return <button onClick={store.increment}>{store.count}</button>;
});
makeAutoObservable
makeAutoObservable
是 mobx
核心库提供的一个 API,用于快速将一个对象转换为可观察的(Observable)状态容器。它的作用是自动推断对象中的属性类型:
- 普通属性 → 转换为
observable
- 方法 → 转换为
action
- Getter 函数 → 转换为
computed
import { makeAutoObservable } from "mobx";
class CounterStore {
count = 0; // 自动变为 observable
constructor() {
makeAutoObservable(this); // 自动处理所有属性和方法
}
increment() { // 自动变为 action
this.count++;
}
get double() { // 自动变为 computed
return this.count * 2;
}
}
(3) useObserver
(局部响应式区域)
在组件内部标记一个需要响应式更新的区域,替代整个组件用 observer
包裹。
import { useObserver } from "mobx-react-lite";
const UserProfile = () => {
const { user } = store;
return useObserver(() => (
<div>
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
));
};
(4) Observer
(组件内局部观察)
类似 useObserver
,但以组件形式包裹需要响应式更新的部分。
import { Observer } from "mobx-react-lite";
const UserProfile = () => {
return (
<div>
<Observer>
{() => (
<span>{store.user.name}</span>
)}
</Observer>
</div>
);
};
(5) autorun
和 reaction
(副作用管理)
在组件中处理副作用(如日志、网络请求),需在 useEffect
中管理生命周期。
import { autorun, reaction } from "mobx";
import { useEffect } from "react";
const UserTracker = () => {
useEffect(() => {
// 自动追踪依赖,当 store.user.id 变化时触发
const disposer = autorun(() => {
console.log("User ID changed:", store.user.id);
});
// 手动定义依赖,并处理新旧值
const reactionDisposer = reaction(
() => store.user.age,
(age, prevAge) => {
console.log(`Age changed from ${prevAge} to ${age}`);
}
);
return () => {
disposer();
reactionDisposer();
};
}, []);
return null;
};
(6) toJS
(转换为普通对象)
将可观察对象转换为普通 JavaScript 对象(常用于传递给外部库或持久化)。
import { toJS } from "mobx";
const DataExporter = observer(() => {
const handleExport = () => {
const plainData = toJS(store.data); // 去除 observability
sendToAPI(plainData);
};
return <button onClick={handleExport}>Export Data</button>;
});
(7) action
和 computed
(状态管理)
在 Store 类或对象中定义状态修改方法和派生值。
import { makeAutoObservable, action, computed } from "mobx";
class UserStore {
users = [];
filter = "";
constructor() {
makeAutoObservable(this);
}
// Action
setFilter = action((filter) => {
this.filter = filter;
});
// Computed
get filteredUsers() {
return this.users.filter(user =>
user.name.includes(this.filter)
);
}
}
(8) configure
(全局配置)
设置 MobX 的全局行为(如严格模式、装饰器兼容性)。
import { configure } from "mobx";
// 强制所有状态修改必须在 action 中
configure({ enforceActions: "observed" });
// 启用装饰器语法支持(如果项目使用装饰器)
configure({ useProxies: "always" });
(9) useAsObservableSource
(已废弃 → 改用 useMemo
)
将外部 props 转换为可观察对象(旧版 API,现推荐其他方式)。
import { useAsObservableSource, observer } from "mobx-react-lite";
const UserProfile = observer((props) => {
const observableProps = useAsObservableSource(props);
return <div>{observableProps.user.name}</div>;
});
3. 异步操作处理
在函数式组件中,使用 runInAction
确保异步后的状态修改被追踪。
import { runInAction } from "mobx";
class PostStore {
posts = [];
loading = false;
fetchPosts = async () => {
this.loading = true;
try {
const data = await fetchPostsAPI();
runInAction(() => {
this.posts = data;
this.loading = false;
});
} catch (error) {
runInAction(() => {
this.loading = false;
});
}
};
}
三、MobX 与 React Hooks
1. 简单场景: useState
足够
如果只是管理 组件内部的简单状态(如一个计数器),useState
完全够用:
function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}
此时引入 MobX 反而是过度设计。
React Hooks 的适用场景:
-
简单组件状态(如表单输入、按钮状态)。
-
小型项目或原型开发(快速迭代,无需长期维护)。
-
无需复杂状态共享(组件间状态通过 Props 传递即可)。
2. 复杂场景:MobX 的碾压性优势
何时该选择 MobX?
-
应用中存在大量跨组件共享状态(如用户信息、主题、全局配置)。
-
需要频繁处理派生状态或复杂计算(如仪表盘、实时数据过滤)。
-
追求极致的渲染性能(避免不必要的子组件更新)。
-
团队熟悉响应式编程概念(降低维护成本)。
如果项目中出现以下信号,就该考虑 MobX 了:
-
你开始频繁使用
useMemo
和memo
来优化性能。 -
组件树中多层级传递状态导致代码难以维护。
-
需要处理大量派生状态或异步副作用。
当遇到以下场景时,MobX 的优势会立刻显现:
(1) 跨组件状态共享
需求: 两个组件(ComponentA
和 ComponentB
)共享同一个计数器状态,点击按钮时同步更新。
-
使用
useState
+ Context痛点:
- 必须使用
memo
包裹子组件,否则任意状态变化都会导致所有子组件重新渲染。 - 当 Context 中的状态复杂时,拆分多个 Context 或优化依赖会非常繁琐。
-
// 使用 Context 共享状态 import React, { useState, createContext, useContext, memo } from "react"; // 定义 Context const CounterContext = createContext<{ count: number; increment: () => void; }>(null!); // Provider 组件 const CounterProvider = ({ children }: { children: React.ReactNode }) => { const [count, setCount] = useState(0); const increment = () => setCount((c) => c + 1); return ( <CounterContext.Provider value={{ count, increment }}> {children} </CounterContext.Provider> ); }; // 子组件 A(需用 memo 避免无效渲染) const ComponentA = memo(() => { const { increment } = useContext(CounterContext); return <button onClick={increment}>+1</button>; }); // 子组件 B(需用 memo 避免无效渲染) const ComponentB = memo(() => { const { count } = useContext(CounterContext); return <span>Count: {count}</span>; }); // 使用组件 const App = () => { return ( <CounterProvider> <ComponentA /> <ComponentB /> </CounterProvider> ); };
- 必须使用
-
使用 MobX直接通过
observer
包裹组件- 组件自动追踪依赖,无需手动优化渲染,精准更新。
- 状态修改直接(无需
setState
),逻辑更集中。
import { observer } from "mobx-react-lite";
import { makeAutoObservable } from "mobx";
import { useMemo } from "react";
// 使用 useMemo + makeAutoObservable 创建 Store
const createCounterStore = () => {
return makeAutoObservable({
count: 0,
increment() {
this.count++;
},
});
};
// 组件 A
const ComponentA = observer(() => {
const store = useMemo(createCounterStore, []); // 实际项目中应通过 Context 共享 Store
return <button onClick={store.increment}>+1</button>;
});
// 组件 B
const ComponentB = observer(() => {
const store = useMemo(createCounterStore, []); // 实际项目中应通过 Context 共享 Store
return <span>Count: {store.count}</span>;
});
// 使用组件(实际项目应通过 Provider 共享 Store)
const App = () => {
return (
<>
<ComponentA />
<ComponentB />
</>
);
};
(2) 派生状态(Computed Values)
需求: 根据用户选择的过滤条件(全部、已完成、未完成),动态显示待办事项列表。
-
使用
useMemo
- 必须手动声明
useMemo
的依赖项(todos
和filter
),若遗漏会导致数据不一致。 - 当派生逻辑复杂时,代码可读性下降。
- 必须手动声明
import { useState, useMemo } from "react";
const TodoListHooks = () => {
const [todos, setTodos] = useState<{ text: string; done: boolean }[]>([]);
const [filter, setFilter] = useState<"all" | "done" | "undone">("all");
// 手动管理派生状态
const filteredTodos = useMemo(() => {
return todos.filter((todo) => {
if (filter === "all") return true;
return filter === "done" ? todo.done : !todo.done;
});
}, [todos, filter]); // 必须显式声明依赖
return (
<div>
<select value={filter} onChange={(e) => setFilter(e.target.value as any)}>
<option value="all">All</option>
<option value="done">Done</option>
<option value="undone">Undone</option>
</select>
<ul>
{filteredTodos.map((todo, index) => (
<li key={index}>{todo.text}</li>
))}
</ul>
</div>
);
};
-
使用 MobX 的
computed
- 派生状态通过
getter
自动追踪依赖,无需手动声明。 - 代码更贴近业务逻辑,可读性更高。
- 派生状态通过
import { observer } from "mobx-react-lite";
import { makeAutoObservable } from "mobx";
import { useMemo } from "react";
// 使用 useMemo + makeAutoObservable 创建 Store
const createTodoStore = () => {
return makeAutoObservable({
todos: [] as { text: string; done: boolean }[],
filter: "all" as "all" | "done" | "undone",
get filteredTodos() { // 自动追踪依赖
return this.todos.filter((todo) => {
if (this.filter === "all") return true;
return this.filter === "done" ? todo.done : !todo.done;
});
},
setFilter(filter: "all" | "done" | "undone") {
this.filter = filter;
},
});
};
const TodoListMobx = observer(() => {
const store = useMemo(createTodoStore, []);
return (
<div>
<select
value={store.filter}
onChange={(e) => store.setFilter(e.target.value as any)}
>
<option value="all">All</option>
<option value="done">Done</option>
<option value="undone">Undone</option>
</select>
<ul>
{store.filteredTodos.map((todo, index) => (
<li key={index}>{todo.text}</li>
))}
</ul>
</div>
);
});
(3) 异步操作与副作用
需求: 从 API 获取用户数据,处理加载状态和错误。
-
使用
useEffect
- 需要手动管理
loading
和error
状态。 - 异步逻辑分散在组件中,难以复用。
- 需要手动管理
import { useState, useEffect } from "react";
const UserListHooks = () => {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState("");
useEffect(() => {
const fetchUsers = async () => {
setLoading(true);
try {
const response = await fetch("/api/users");
const data = await response.json();
setUsers(data);
setError("");
} catch (err) {
setError("Failed to fetch users");
} finally {
setLoading(false);
}
};
fetchUsers();
}, []);
if (loading) return <div>Loading...</div>;
if (error) return <div>{error}</div>;
return <div>{users.map((user) => user.name)}</div>;
};
-
使用 MobX 的
autorun
/reaction
自动追踪依赖,声明式处理副作用。- 异步逻辑封装在 Store 中,可复用且易于测试。
- 使用
runInAction
确保状态修改被追踪,代码更安全。
import { observer } from "mobx-react-lite";
import { makeAutoObservable, runInAction } from "mobx";
import { useMemo } from "react";
// 使用 useMemo + makeAutoObservable 创建 Store
const createUserStore = () => {
return makeAutoObservable({
users: [] as any[],
loading: false,
error: "",
async fetchUsers() {
this.loading = true;
try {
const response = await fetch("/api/users");
const data = await response.json();
runInAction(() => {
this.users = data;
this.error = "";
});
} catch (err) {
runInAction(() => {
this.error = "Failed to fetch users";
});
} finally {
runInAction(() => {
this.loading = false;
});
}
},
});
};
const UserListMobx = observer(() => {
const store = useMemo(createUserStore, []);
useEffect(() => {
store.fetchUsers();
}, [store]);
if (store.loading) return <div>Loading...</div>;
if (store.error) return <div>{store.error}</div>;
return <div>{store.users.map((user) => user.name)}</div>;
});
(4) 性能优化
-
使用
useState
父组件状态变化会导致所有子组件重新渲染,需手动用memo
或拆分组件。 -
使用 MobX
observer
组件仅在依赖的状态变化时更新,粒度更细,性能更高。
3. MobX 的核心价值总结
场景 | React Hooks (useState + Context) | MobX |
---|---|---|
简单组件状态 | ✅ 简单直接 | ❌ 过度设计 |
跨组件状态共享 | ⚠️ 需手动优化,易导致性能问题 | ✅ 自动精准更新 |
派生状态 | ⚠️ 依赖 useMemo ,需手动管理依赖 | ✅ 自动追踪,声明式 |
异步/副作用 | ⚠️ 需 useEffect 和清理逻辑 | ✅ 声明式,自动依赖追踪 |
性能优化 | ⚠️ 依赖 memo 和拆分组件 | ✅ 细粒度更新,零成本优化 |
代码复杂度 | ⚠️ 复杂逻辑时代码臃肿 | ✅ 逻辑集中,高可维护性 |
四、最佳实践与常见问题
1. 状态组织
-
单一 Store vs 多 Store根据项目复杂度拆分 Store(如
UserStore
,PostStore
,UIStore
)。 -
Context API 共享 Store使用 React Context 全局共享 Store。
import { createContext, useContext } from "react";
import { UserStore } from "./stores";
const StoreContext = createContext<UserStore>(null!);
const App = () => {
const userStore = new UserStore();
return (
<StoreContext.Provider value={userStore}>
<ChildComponent />
</StoreContext.Provider>
);
};
const ChildComponent = () => {
const userStore = useContext(StoreContext);
return <div>{userStore.username}</div>;
};
2. 性能优化
-
避免不必要的渲染: 使用
observer
或Observer
精细控制渲染范围。 -
细粒度拆分组件: 将大组件拆分为多个观察者组件,减少重渲染范围。
3. 常见陷阱
- 直接修改状态: 确保在
action
中修改状态(严格模式下会报错)。 - 未清理副作用:
autorun
和reaction
需在useEffect
的清理函数中销毁。 - 过度使用:
toJS
仅在必要时转换,避免破坏响应式。