MobX状态管理:简洁而强大的状态机

简介: MobX 是一种简洁高效的状态管理库,通过声明式的方式管理应用状态,使数据变化自动同步到视图。它利用 `@observable` 创建响应式数据,`@computed` 实现自动更新的计算属性,`@action` 确保状态安全变更。结合 `mobx-react`,可在 React 组件中使用 `observer` 自动响应状态更新。MobX 内部采用代理与访问者模式追踪依赖,确保最小化更新,提升性能。支持 TypeScript,提供类型安全与代码提示。

MobX 是一个用于构建可响应的数据模型的库,它提供了一种声明式的方式来管理状态,使得数据的变化能够自动更新相关的视图。

创建可观察状态(Observable State)

MobX使用@observable装饰器来创建可观察的对象、数组或基本类型,当它们发生变化时,依赖它们的观察者会自动更新。

   import {
    observable } from 'mobx';

   class Todo {
   
     @observable title;
     @observable completed;

     constructor(title) {
   
       this.title = title;
       this.completed = false;
     }
   }

   const todo1 = new Todo("Learn MobX");

可响应的计算值(Computed Values)

使用@computed装饰器创建基于其他可观察值的计算值,这些计算值会根据依赖关系自动更新。

   class TodoList {
   
     @observable todos = [];
     @computed get completedCount() {
   
       return this.todos.filter(todo => todo.completed).length;
     }
   }

   const todoList = new TodoList();
   todoList.todos.push(todo1);

动作(Actions)

使用@action装饰器确保状态的改变发生在受控的环境中,这有助于避免在不恰当的地方修改状态。

   class TodoList {
   
     // ...
     @action addTodo(title) {
   
       this.todos.push(new Todo(title));
     }

     @action toggleTodo(index) {
   
       this.todos[index].completed = !this.todos[index].completed;
     }
   }

观察者(Observers)

在React中,使用mobx-react库的observer高阶组件或useObserver Hook,使组件对状态变化作出反应。

   import React from 'react';
   import { observer } from 'mobx-react-lite';

   const TodoListComponent = observer(({ todoList }) => (
     <ul>
       {todoList.todos.map((todo, index) => (
         <li key={index}>
           {todo.title} - {todo.completed ? 'Completed' : 'Not completed'}
         </li>
       ))}
     </ul>
   ));

   const App = () => {
     const todoList = new TodoList();
     return <TodoListComponent todoList={todoList} />;
   };

反应式编程(Reactive Programming)

MobX的核心在于其反应式系统,当数据变化时,所有依赖它的计算值和观察者都会自动更新,无需手动调用setState。反应式编程是一种编程范式,它强调数据流和变化的传播,使得程序能够自动响应数据变化。

可观察对象(Observables)

MobX使用@observable装饰器或observable函数来创建可观察的值。当这些值发生变化时,依赖它们的任何计算或视图都会自动更新。

   import {
    observable } from 'mobx';

   class Counter {
   
     @observable count = 0;
   }

   const counter = new Counter();

   // 观察者自动更新
   autorun(() => {
   
     console.log(`当前计数是: ${
     counter.count}`);
   });

   // 修改计数
   counter.count++;

计算值(Computed Values)

@computed装饰器用于创建基于可观察值的计算值。计算值在依赖的可观察值变化时会自动更新。

   class Counter {
   
     // ...
     @computed get isEven() {
   
       return this.count % 2 === 0;
     }
   }

   // 计算值自动更新
   autorun(() => {
   
     console.log(`当前计数是否为偶数: ${
     counter.isEven}`);
   });

   // 修改计数
   counter.count++;

反应(Reactions)

使用autorunreactionwhen函数创建反应,这些函数会在数据变化时自动执行。autorun每当依赖的可观察值变化时运行,而reaction则在特定条件变化时运行。

   // 反应式更新UI
   reaction(
     () => counter.count,
     (newCount) => {
   
       updateUI(newCount);
     },
   );

   // 基于条件的反应
   when(
     () => counter.count > 10,
     () => {
   
       console.log('计数超过10了!');
     },
   );

动作(Actions)

@action装饰器或action函数用于标记状态更改的函数。这确保了状态在受控环境中改变,防止了意外的副作用。

   class Counter {
   
     // ...
     @action increment() {
   
       this.count++;
     }
   }

   counter.increment();

自动追踪依赖(Automatic Dependency Tracking) MobX使用代理(proxies)和访问者模式来自动追踪计算值和反应的依赖。这意味着你不需要手动处理依赖关系,系统会自动处理。

跟踪依赖(Dependency Tracking)

MobX使用代理(proxies)和访问者模式来跟踪哪些计算值和观察者依赖于哪些可观察状态,从而实现高效的更新。

代理(Proxies)

MobX使用ES6的Proxy对象来创建可观察对象的代理。Proxy可以拦截对象的访问和修改操作,这使得MobX能够监听到何时读取或修改可观察状态。

   const observableValue = observable(42);
   const proxyValue = new Proxy(observableValue, mobxHandler); // mobxHandler包含了拦截逻辑

访问者模式(Visitor Pattern)

当访问可观察对象的属性时,MobX会记录访问路径,这称为访问者模式。它创建了一个依赖树,表示哪些计算值或反应函数依赖于哪些可观察状态。

   class ObservableObject {
   
     @observable prop;
     // ...
   }

   const obj = new ObservableObject();
   autorun(() => {
   
     console.log(obj.prop);
   });

   // 访问 obj.prop 时,MobX记录了这个依赖
   obj.prop = "new value";

变化通知(Change Notifications)

当可观察状态改变时,MobX会通知所有依赖于这个状态的计算值和反应函数。由于依赖已经被记录,所以只有真正受到影响的计算和反应才会被触发。

最小化更新(Minimized Updates)

依赖追踪确保了只有真正需要更新的计算值和反应函数才会执行。这提高了性能,因为只有在必要时才会重新计算。

优化(Optimizations)

MobX还提供了优化机制,例如,通过使用asFlat、asReference或asStructure等方法,可以控制代理对象如何处理变化,以进一步提高性能。

中间件集成

尽管MobX并不像Redux那样与中间件紧密集成,但你可以使用mobx-react-devtools来监控状态变化,提供类似的时间旅行调试功能。

安装插件

使用npm或yarn安装mobx-react-devtools

   npm install --save mobx-react-devtools
   # 或
   yarn add mobx-react-devtools

在你的应用中引入

在你的主应用程序文件(通常是index.js或App.js)中,导入并插入mobxReactDevTools组件:

   import { Provider } from 'mobx-react';
   import { mobxReactDevTools } from 'mobx-react-devtools';

   // 假设你已经有了你的store
   const store = new YourStore();

   ReactDOM.render(
     <Provider {...store}>
       <YourApp />
       {/* 在你的应用下方添加DevTools */}
       <mobxReactDevTools />
     </Provider>,
     document.getElementById('root')
   );

启用开发者工具

一旦你的应用运行起来,你可以在浏览器的开发者工具中看到一个新的面板,显示你的MobX状态和变更历史。在Chrome或Firefox中,通常可以通过打开开发者工具,然后选择“.mobx-react-devtools”或“Extensions”面板来找到它。

时间旅行调试(Time Travel Debugging)

虽然mobx-react-devtools不直接提供时间旅行调试,但你可以使用mobx-state-tree库,它与MobX兼容,并提供了时间旅行功能。mobx-state-tree创建了一个可逆的操作历史,允许你回放和重播状态的变更。

TypeScript支持

MobX与TypeScript有很好的集成,可以提供类型安全和更好的代码提示。

类型注解(Type Annotations)

在TypeScript中,你可以为可观察对象、计算值和动作添加类型注解,确保类型安全。

   import {
    observable, computed, action } from 'mobx';

   class Counter {
   
     @observable count: number = 0;

     @computed
     get isEven(): boolean {
   
       return this.count % 2 === 0;
     }

     @action
     increment(): void {
   
       this.count++;
     }
   }

接口(Interfaces)

通过定义接口,你可以为可观察对象创建更复杂的类型结构,确保数据模型的一致性。

   interface Todo {
   
     id: number;
     title: string;
     completed: boolean;
   }

   class TodoStore {
   
     @observable todos: Todo[] = [];

     // ...
   }

类型推断(Type Inference)

TypeScript会自动推断计算值的类型,基于它们的依赖。

类型保护(Type Guards)

你可以使用类型保护函数来确保在类型安全的上下文中访问可观察对象。

   function isTodo(item: any): item is Todo {
   
     return item && typeof item.id === 'number' && typeof item.title === 'string';
   }

   const todo = getTodoFromSomewhere();

   if (isTodo(todo)) {
   
     // 在这里,TypeScript知道`todo`是`Todo`类型
     console.log(todo.title);
   }

类型扩展(Type Extensions)

你可以扩展ObservableObject, ObservableArray, 和 ObservableMap的类型,以便更好地描述你的数据。

makeObservable和makeAutoObservable

在MobX 6中,推荐使用makeObservablemakeAutoObservable来初始化可观察状态,它们提供了更好的类型安全和自动类型推断。

   class TodoStore {
   
     private todos: Todo[] = [];

     constructor() {
   
       makeAutoObservable(this);
     }

     // ...
   }

反应式数据流(Reactive Data Flow)

MobX的反应式数据流意味着数据变化会自动传播到依赖的计算和视图,这使得数据模型和UI之间的关系更加清晰。

import React from 'react';
import { render } from 'react-dom';
import { observable, computed, reaction } from 'mobx';
import { Provider, observer } from 'mobx-react';

// 创建可观察对象
const counterStore = {
  @observable count: 0,

  @action increment() {
    this.count++;
  },

  @computed get doubleCount() {
    return this.count * 2;
  },
};

// 创建一个反应,当count变化时,打印doubleCount
reaction(
  () => counterStore.count,
  (newCount) => {
    console.log(`Double count: ${counterStore.doubleCount}`);
  },
);

// 创建一个React组件,观察count的变化
const Counter = observer(({ store }: { store: typeof counterStore }) => (
  <div>
    <p>Count: {store.count}</p>
    <button onClick={store.increment}>Increment</button>
    <p>Double Count: {store.doubleCount}</p>
  </div>
));

// 渲染应用
render(
  <Provider counterStore={counterStore}>
    <Counter />
  </Provider>,
  document.getElementById('root'),
);

在这个例子中,counterStore是一个包含可观察状态的对象,count属性是可观察的。doubleCount是一个计算值,它基于count自动计算。当count增加时,doubleCountCounter组件都会自动更新,无需手动调用setState。

reaction函数创建了一个观察者,当count改变时,它会打印出doubleCount的新值。这样,数据模型的改变就会自动传播到UI和任何依赖它的计算,形成了一个清晰的反应式数据流。

反应式函数(Reactive Functions)

使用autorun、reaction或when函数,你可以创建基于数据变化的自动执行函数。这些函数会在相关数据改变时自动运行,直到满足特定条件或被手动停止。

    const disposer = reaction(
      () => todoList.completedCount,
      (completedCount) => console.log(`There are ${
     completedCount} completed todos`),
    );

    // Later, to stop the reaction:
    disposer();

响应式API调用

如果你需要在API调用中使用MobX,可以使用runInAction包裹异步操作,确保状态更新在正确的作用域内。

    async function fetchData() {
   
      await someAsyncCall().then(data => {
   
        runInAction(() => {
   
          // 更新状态
          myStore.setData(data);
        });
      });
    }

微核架构(Microkernel Architecture)

MobX的核心很小,可以根据需要选择性地引入额外的功能,如mobx-state-treemobx-react-form,以增强特定场景下的状态管理和表单处理。

mobx-state-tree

mobx-state-tree是基于MobX的状态管理库,它提供了强大的类型安全、状态快照、时间旅行调试和丰富的异常处理。

   import {
    types, onSnapshot, IStateTreeNode } from 'mobx-state-tree';

   const Todo = types.model({
   
     title: types.string,
     completed: types.boolean,
   });

   const TodoStore = types.model({
   
     todos: types.array(Todo),
   }).actions(self => ({
   
     addTodo(title: string) {
   
       self.todos.push(Todo.create({
    title, completed: false }));
     },
   }));

   const store = TodoStore.create({
    todos: [] });

   onSnapshot(store, (snapshot) => {
   
     console.log('State snapshot:', snapshot);
   });

   store.addTodo('Learn MobX');

mobx-react-form

mobx-react-form是一个用于创建和管理表单的库,它与MobX集成良好,提供了验证、提交和重置等功能。

   import React from 'react';
   import { Form, Field } from 'mobx-react-form';
   import { observable, action } from 'mobx';
   import { Provider, observer } from 'mobx-react';

   const schema = {
     name: 'form',
     fields: ['name', 'email'],
     labels: { name: 'Name', email: 'Email' },
     validators: {
       name: 'required',
       email: 'required|email',
     },
   };

   class MyForm extends Form {
     constructor(values = {}) {
       super(schema, values);
       this.plugins({
         dvr: {},
       });
     }
   }

   const form = new MyForm();

   @observer
   class MyComponent extends React.Component {
     @observable submitting = false;

     @action submitForm = () => {
       this.submitting = true;
       if (form.validate()) {
         alert('Form submitted successfully!');
         form.reset();
       } else {
         form.focus();
       }
       this.submitting = false;
     };

     render() {
       return (
         <Provider form={form}>
           <form onSubmit={this.submitForm}>
             <Field type="text" name="name" />
             <Field type="email" name="email" />
             <button type="submit" disabled={this.submitting}>
               Submit
             </button>
           </form>
         </Provider>
       );
     }
   }

   render(<MyComponent />, document.getElementById('root'));

这两个库扩展了MobX的核心功能,分别针对状态管理和表单处理提供了更高级的抽象。通过这种微核架构,你可以根据项目的具体需求选择合适的工具,保持项目的轻量级和模块化。

与其他库的集成

MobX不仅适用于React,也可以与Vue.js、Angular和其他库集成。此外,它还可以与Redux或其他状态管理库共存,用于特定的场景。

热重载和开发工具

MobX与mobx-react-devtools插件配合使用,提供了在开发过程中查看数据流、跟踪依赖和性能分析的能力,支持热重载,方便快速迭代。

性能优化

MobX的响应式系统会自动跟踪依赖,仅在必要时更新视图,这通常比手动触发更新更高效。然而,如果遇到性能问题,可以使用makeObservable或makeAutoObservable的asStructure或asReference选项,以及trackTransitions来调整性能。

设计模式

MobX鼓励使用如MVC(Model-View-Controller)、MVVM(Model-View-ViewModel)或MST(MobX State Tree)等设计模式,以更好地组织和管理状态。

2500G计算机入门到高级架构师开发资料超级大礼包免费送!

相关文章
|
16天前
|
存储 JavaScript 前端开发
Redux 做状态管理和发布订阅模式有什么区别
【10月更文挑战第26天】Redux和发布订阅模式在状态管理方面各有优缺点,它们适用于不同的应用场景和开发需求。在实际开发中,需要根据具体项目的特点和要求,选择合适的状态管理方式,或者在某些情况下将二者结合使用,以达到最佳的开发效果和性能表现。
95 65
|
15天前
|
资源调度 JavaScript 前端开发
使用 Redux 进行状态管理
【10月更文挑战第26天】从定义 Action 和 Reducer,到创建 Store,再到将 Redux 与 React 组件进行连接,以及处理异步操作,Redux 提供了一种清晰、可预测的方式来管理应用程序的状态,使得应用的状态变化更加易于理解和维护,尤其适用于大型复杂的应用开发。在实际使用过程中,还可以根据项目的具体需求,进一步优化 Redux 的使用,如使用 `redux-immutable` 来处理不可变数据、使用 `redux-devtools` 进行调试等,以提高开发效率和应用性能。
33 9
|
1月前
|
存储 JavaScript 前端开发
Redux 状态管理入门
本文介绍了 Redux,一个广泛使用的 JavaScript 状态管理库,重点讲解了其核心概念(如 Store、Action、Reducer 等)、基本使用方法、常见问题及解决策略,并通过代码示例详细说明了如何在 React 应用中集成和使用 Redux。
30 1
|
30天前
|
存储 移动开发 JavaScript
vuex的工作流程,模块化使用案例分享,及状态持久化
vuex的工作流程,模块化使用案例分享,及状态持久化
24 0
|
3月前
|
存储 JavaScript 前端开发
不要滥用Pinia和Redux了!多组件之间交互可以手写一个调度器!
【8月更文挑战第24天】不要滥用Pinia和Redux了!多组件之间交互可以手写一个调度器!
59 2
不要滥用Pinia和Redux了!多组件之间交互可以手写一个调度器!
|
6月前
|
存储 JavaScript 前端开发
【第36期】一文学会Redux状态管理
【第36期】一文学会Redux状态管理
114 0
|
6月前
|
前端开发 JavaScript
深入理解React中的useReducer:管理复杂状态逻辑的利器
深入理解React中的useReducer:管理复杂状态逻辑的利器
|
6月前
|
存储 前端开发 JavaScript
【思维扩展】状态机与 React 中的状态
【思维扩展】状态机与 React 中的状态
|
6月前
|
JavaScript 前端开发
【干货分享】轻松入门 Redux:解密状态管理的神奇世界
【干货分享】轻松入门 Redux:解密状态管理的神奇世界
|
6月前
|
资源调度 JavaScript
如何使用 Vuex 管理应用的状态?
如何使用 Vuex 管理应用的状态?
31 3