跳到主要内容

Mobx

介绍

官网:https://mobx.js.org

MobX 是一个简单且强大的状态管理库,主要用于管理应用程序中的状态。它通过响应式编程的思想,让状态的变化自动反映到 UI 上,而无需手动操作 DOM 或过多样板代码。

优点:

  1. 简单易用:Mobx 的 API 简单直观,易于学习和使用
  2. 响应式编程:通过自动追踪依赖关系,Mobx 可以在状态变化时自动更新 UI
  3. 高性能:Mobx 只会更新那些真正需要更新的组件,避免不必要的渲染
  4. 灵活性:可以与任何框架或库一起使用,不仅限于 React

MobX 的核心概念

Mobx 的核心是可观察对象(observable)、动作(actions)、计算值(computed values)和反应(reactions)。可观察对象是 Mobx 的状态存储,动作是改变状态的方法,计算值是从状态派生的值,反应是自动更新 UI 的机制。

  1. Observable(可观察状态) 使用 observable@observable 将数据标记为可观察的,当这些数据发生变化时,MobX 会自动通知依赖它的组件或函数。
  2. Computed(计算属性) 使用 computed@computed 定义依赖于 observable 数据的衍生值,当相关 observable 数据变化时,计算属性会自动更新。
  3. Reactions(反应) MobX 的反应机制(如 autorun, reaction, 或 React 组件中的 observer)会自动响应状态变化并执行副作用,比如更新 UI。
  4. Actions(动作) 使用 action@action 定义修改状态的操作,推荐将所有状态变更封装在 action 中,以确保代码的可追溯性和性能优化。

MobX 与 React 的结合

在 React 中,MobX 通常通过 mobx-reactmobx-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>
);