Mobx
介绍
MobX 是一个简单且强大的状态管理库,主要用于管理应用程序中的状态。它通过响应式编程的思想,让状态的变化自动反映到 UI 上,而无需手动操作 DOM 或过多样板代码。
优点:
- 简单易用:Mobx 的 API 简单直观,易于学习和使用
- 响应式编程:通过自动追踪依赖关系,Mobx 可以在状态变化时自动更新 UI
- 高性能:Mobx 只会更新那些真正需要更新的组件,避免不必要的渲染
- 灵活性:可以与任何框架或库一起使用,不仅限于 React
MobX 的核心概念
Mobx 的核心是可观察对象(observable)、动作(actions)、计算值(computed values)和反应(reactions)。可观察对象是 Mobx 的状态存储,动作是改变状态的方法,计算值是从状态派生的值,反应是自动更新 UI 的机制。
- Observable(可观察状态)
使用
observable
或@observable
将数据标记为可观察的,当这些数据发生变化时,MobX 会自动通知依赖它的组件或函数。 - Computed(计算属性)
使用
computed
或@computed
定义依赖于 observable 数据的衍生值,当相关 observable 数据变化时,计算属性会自动更新。 - Reactions(反应) MobX 的反应机制(如 autorun, reaction, 或 React 组件中的 observer)会自动响应状态变化并执行副作用,比如更新 UI。
- Actions(动作)
使用
action
或@action
定义修改状态的操作,推荐将所有状态变更封装在 action 中,以确保代码的可追溯性和性能优化。
MobX 与 React 的结合
在 React 中,MobX 通常通过 mobx-react
或 mobx-react-lite
集成。使用 observer 高阶组件或 hooks(如 useObserver)将 React 组件变为响应式的,当 observable 数据变化时,组件会自动重新渲染。
mobx-react-lite
仅支持函数组件,而mobx-react
还支持类组件。
实现一个 TodoList
安装依赖:
pnpm add mobx mobx-react-lite
1、创建 store
store.ts
import { makeAutoObservable } from 'mobx';
export interface Todo {
id: number;
text: string;
completed: boolean;
}
class TodoStore {
todos: Todo[] = [];
constructor() {
makeAutoObservable(this); // 自动将属性和方法变为可观察的
}
// 添加任务
addTodo(text: string) {
this.todos.push({
id: Date.now(),
text,
completed: false
});
}
// 切换任务完成状态
toggleTodo(id: number) {
const todo = this.todos.find(t => t.id === id);
if (todo) {
todo.completed = !todo.completed;
}
}
// 删除任务
removeTodo(id: number) {
this.todos = this.todos.filter(t => t.id !== id);
}
// 计算未完成任务数量
get unfinishedCount() {
return this.todos.filter(t => !t.completed).length;
}
}
export default new TodoStore();
2、创建 TodoList 组件
TodoList.tsx
import { useState } from 'react';
import { observer } from 'mobx-react-lite';
import todoStore from './store';
import TodoItem from './TodoItem';
const TodoList = observer(() => {
const [inputValue, setInputValue] = useState('');
const handleAddTodo = () => {
if (inputValue.trim()) {
todoStore.addTodo(inputValue);
setInputValue('');
}
};
return (
<div className="max-w-md mx-auto mt-10 p-6 bg-white rounded-lg shadow-lg">
<h1 className="text-2xl font-bold mb-4 text-center">Todo List</h1>
<div className="flex mb-4">
<input
type="text"
value={inputValue}
onChange={e => setInputValue(e.target.value)}
placeholder="输入任务"
className="flex-1 p-2 border rounded-l-md focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
<button onClick={handleAddTodo} className="p-2 bg-blue-500 text-white rounded-r-md hover:bg-blue-600">
添加
</button>
</div>
<ul className="list-none">
{todoStore.todos.map(todo => (
<TodoItem key={todo.id} todo={todo} />
))}
</ul>
<p className="mt-4 text-gray-600">
未完成任务数: <span className="font-semibold">{todoStore.unfinishedCount}</span>
</p>
</div>
);
});
export default TodoList;
3、创建 TodoItem 组件
TodoItem.tsx
import { observer } from 'mobx-react-lite';
import todoStore from './store';
const TodoItem = observer(({ todo }: { todo: { id: number; text: string; completed: boolean } }) => {
return (
<li
className={`flex items-center justify-between p-2 border-b ${todo.completed ? 'line-through text-gray-500' : ''}`}
>
<div className="flex items-center">
<input
type="checkbox"
checked={todo.completed}
onChange={() => todoStore.toggleTodo(todo.id)}
className="mr-2"
/>
<span>{todo.text}</span>
</div>
<button onClick={() => todoStore.removeTodo(todo.id)} className="text-red-500 hover:text-red-700">
删除
</button>
</li>
);
});
export default TodoItem;
然后将 TodoList 组件引入到 App.tsx 中,将 App.tsx 渲染到页面中。
main.ts
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App.tsx';
ReactDOM.createRoot(document.getElementById('root')!).render(
// <React.StrictMode>
<App />
// </React.StrictMode>
);