前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >MobX 和 React 十分钟快速入门

MobX 和 React 十分钟快速入门

作者头像
疯狂的技术宅
发布于 2019-03-27 07:19:32
发布于 2019-03-27 07:19:32
1.4K00
代码可运行
举报
文章被收录于专栏:京程一灯京程一灯
运行总次数:0
代码可运行

MobX 是一种简单的、可扩展的、久经考验的状态管理解决方案。

这个教程将在十分钟内向你详解 MobX 的所有重要概念。MobX 是一个独立的库,但是大部分人将它和 React 共同使用,所以本教程将重点讲解他们的结合使用。

核心理念

State 是所有应用的核心,没有任何途径比“创建不稳定的 State 或者创建与周围本地变量不同步的State”更容易产生 bug 丛生、不可管理的应用了。

因此,许多 State 管理解决方案试图限制可以变更状态的方法,例如使其不可变(immutable)。

但这带来了新的问题:数据需要规范化,无法保证引用的完整性,使用原型之类的强大概念几乎是不可能的。

MobX 通过解决根本问题重新简化了 State 管理工作:我们根本无法创建不稳定的 State。

达到这一目标的策略很简单:保证从应用程序状态派生出的所有内容都可以被自动地推导出来。

原理上,MobX 将你的应用看做是一个电子表格:

  1. 首先,我们看应用状态(application state)。对象,数组,原型,引用组成了你的应用程序的 model。
  2. 其次,看看推导(derivations)。讲道理,所有可以通过应用程序 state 自动计算出来的值都算推导。这些推导或计算的值,范围包括从简单的值(如未完成的 todo 数量),到复杂的值(如一个表示 todo 的可视化 HTML)。从电子表格的角度看:这些是应用程序的公式和图表。
  3. 响应(Reactions) 与推导很类似。主要的区别是这些函数不产生值,而是自动地执行一些任务,这些任务通常与 I/O 相关。他们保证了在正确的时间自动地更新 DOM 或者发起网络请求。
  4. 最后我们看看 行动(actions)。行动是所有改变 state 的事情。MobX 将保证所有由你的操作触发的 state 变更都可以被所有的派生和响应处理。这个过程是同步且无故障的。

一个简单的 todo store

理论讲完了,实际操作试试可能比仔细阅读上面的东西更能说明问题。出于创意,让我们从一个非常简单的 todo store 做起。注意:下面所有的代码块是可编辑的,可以点击 run code 按钮执行它们(译者注:臣妾做不到……详细执行结果请参考原文)。下面是一个很简单的 TodoStore 用来管理待办事项。还没有加入 MobX。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class TodoStore {
    todos = [];

    get completedTodosCount() {
        return this.todos.filter(
            todo => todo.completed === true
        ).length;
    }

    report() {
        if (this.todos.length === 0)
            return "<none>";
        return `Next todo: "${this.todos[0].task}". ` +
            `Progress: ${this.completedTodosCount}/${this.todos.length}`;
    }

    addTodo(task) {
        this.todos.push({
            task: task,
            completed: false,
            assignee: null
        });
    }
}

const todoStore = new TodoStore();

我们刚刚创建了一个包含 待办事项 列表的一个 todoStore 实例。是时候给它填充一些对象了。为了保证我们可以看到我们改变的影响,我们在每个变更之后调用 todoStore.report 并打印它。注意这个报告故意只打印第一个任务。这使得这个例子看起来有点别扭,但是你将看到它可以很好地说明 MobX 的依赖跟踪是动态的。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
todoStore.addTodo("read MobX tutorial");
console.log(todoStore.report());

todoStore.addTodo("try MobX");
console.log(todoStore.report());

todoStore.todos[0].completed = true;
console.log(todoStore.report());

todoStore.todos[1].task = "try MobX in own project";
console.log(todoStore.report());

todoStore.todos[0].task = "grok MobX tutorial";
console.log(todoStore.report());

Becoming reactive

到目前为止,这段代码没什么特殊的。但是如果我们不需要明确地调用 report,而是生命我们希望它在每次状态的改变时被调用呢?这将使我们不再需要纠结在所有可能影响报告的地方调用 report。我们想要保证最新的报告被打印。但是我们不想纠结于怎么去组织它。

值得庆幸的是,这正是 MobX 可以为你做到的。自动执行完全依赖 state 的代码。因此我们的 report 函数像电子表格中的图表一样自动更新。为了实现这一目标, TodoStore 需要变成可监视的(observable)以保证 MobX 可以追踪到所有改变。让我们一起改改代码来实现它。

进一步, completedTodosCount 属性可以由 todo list 自动推导而来。我们可以使用 @observable@computed 装饰器为一个对象增加 observable 属性:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class ObservableTodoStore {
    @observable todos = [];
    @observable pendingRequests = 0;

    constructor() {
        mobx.autorun(() => console.log(this.report));
    }

    @computed get completedTodosCount() {
        return this.todos.filter(
            todo => todo.completed === true
        ).length;
    }

    @computed get report() {
        if (this.todos.length === 0)
            return "<none>";
        return `Next todo: "${this.todos[0].task}". ` +
            `Progress: ${this.completedTodosCount}/${this.todos.length}`;
    }

    addTodo(task) {
        this.todos.push({
            task: task,
            completed: false,
            assignee: null
        });
    }
}


const observableTodoStore = new ObservableTodoStore();

搞定啦!我们为 MobX 标记了一些 @observable 属性,这些属性的值可以随时改变。计算值是用 @computed 标记以表示他们可以由 state 推导出来。

pendingRequestsassignee 属性现在还没被使用,但是将会在本教程的后面被使用。为了简洁,本页中的例子都使用了 ES6、JSX 和装饰器(decorators)。但是不要担心,MobX 中所有的装饰器对应有 ES5 的形式。

在构造函数中,我们创建了一个小函数来打印 report 并用 autorun 包裹它。autorun 创建了一个 响应(Reaction) 并执行一次,之后这个函数中任何 observable 数据变更时,响应都会被自动执行。由于 report 使用了 observable todos 属性,所以它将会在所有合适的时刻打印 report。下面的例子可以说明这一点,只需要点击一下 run 按钮(译者:……):

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
observableTodoStore.addTodo("read MobX tutorial");
observableTodoStore.addTodo("try MobX");
observableTodoStore.todos[0].completed = true;
observableTodoStore.todos[1].task = "try MobX in own project";
observableTodoStore.todos[0].task = "grok MobX tutorial";

很好玩对不对? report 自动地打印了,这个过程是自动的且没有中间变量泄露。如果你仔细研究日志,你会发现第四行没有生成新的日志行。因为 report 并没有 真正地 因为重命名而改变,尽管底层数据确实变了。而变更第一个 todo 的名字改变了 report,因为它的 name 被 report 使用了。这充分地说明了 autorun不只监听了 todo 数组,而且还监听了 todo 元素中的个别属性。

让 React 变得有响应(reactive)

好了,目前为止我们创建了一个简单的响应式 report。是时候在这个 store 周围构造一个响应式的用户接口了。React 组件无法对外界作出反应(除了自己的名字)。 mobx-react 包的 @observer 装饰器通过将 React 组件的 render 方法包裹在 autorun 中解决了这一问题,它自动地保持你的组件和 state 同步。理论上这和我们之前对 report 的做法没什么区别。

下面的例子定义了一些 React 组件。这些组件中只有 @observer 是属于的 MobX 的。但它足以保证所有的组件都可以在相关数据变更时独立地重新渲染。你不再需要调用 setState,也不必考虑如何通过配置选择器或高阶组件来订阅应用程序 state 的适当部分。可以说,所有的组件都变得智能化。不过他们是以愚蠢的声明的方式定义的。

点击 Run code 按钮查看下面代码的结果。这个例子是可编辑的,所以你可以随便在里面玩耍。试着删掉所有的 @oberver 或者只删掉装饰 TodoView 的那一个。右边预览中的数字会在每次组件重新渲染的时候高亮。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@observer
class TodoList extends React.Component {
  render() {
    const store = this.props.store;
    return (
      <div>
        { store.report }
        <ul>
        { store.todos.map(
          (todo, idx) => <TodoView todo={ todo } key={ idx } />
        ) }
        </ul>
        { store.pendingRequests > 0 ? <marquee>Loading...</marquee> : null }
        <button onClick={ this.onNewTodo }>New Todo</button>
        <small> (double-click a todo to edit)</small>
        <RenderCounter />
      </div>
    );
  }

  onNewTodo = () => {
    this.props.store.addTodo(prompt('Enter a new todo:','coffee plz'));
  }
}

@observer
class TodoView extends React.Component {
  render() {
    const todo = this.props.todo;
    return (
      <li onDoubleClick={ this.onRename }>
        <input
          type='checkbox'
          checked={ todo.completed }
          onChange={ this.onToggleCompleted }
        />
        { todo.task }
        { todo.assignee
          ? <small>{ todo.assignee.name }</small>
          : null
        }
        <RenderCounter />
      </li>
    );
  }

  onToggleCompleted = () => {
    const todo = this.props.todo;
    todo.completed = !todo.completed;
  }

  onRename = () => {
    const todo = this.props.todo;
    todo.task = prompt('Task name', todo.task) || todo.task;
  }
}

ReactDOM.render(
  <TodoList store={ observableTodoStore } />,
  document.getElementById('reactjs-app')
);

下一个例子完美地展现了我们不需要做任何别的事情就可以改变我们的数据。MobX 将会从 store 的 state 中自动地派生并更新用户界面相关的部分。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const store = observableTodoStore;
store.todos[0].completed = !store.todos[0].completed;
store.todos[1].task = "Random todo " + Math.random();
store.todos.push({ task: "Find a fine cheese", completed: true });
// etc etc.. add your own statements here...

使用引用

到目前为止,我们已经创建了 observable 对象(包括原型和普通对象),数组和原语。你可能会惊讶,MobX 是如何操作这些引用的?是我们的 state 可以被用于创建一个图表吗?在上面的例子中,你可能发现 todo 上有一个 assignee 属性。让我们通过引入另一个包含人员信息的“store”(其实,它只是一个美化的数组)来给他们一些值,并将任务分配给他们。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var peopleStore = mobx.observable([
    { name: "Michel" },
    { name: "Me" }
]);
observableTodoStore.todos[0].assignee = peopleStore[0];
observableTodoStore.todos[1].assignee = peopleStore[1];
peopleStore[0].name = "Michel Weststrate";

我们现在拥有两个独立的 store。一个包含人员信息,另一个包含 todo 信息。为人员 store 中的一个人赋予一个 assignee,我们只需要添加一个引用。这些改变会被 TodoView 自动获取。在 MobX 的帮助下,我们不需要先格式化数据并写相应的选择器以保证我们的组件可以被更新。实际上,甚至是数据的存储位置也并不重要。只要对象被设置为 obervable,MobX 将可以追踪他们。真实的 JavaScript 引用将会起作用。如果它们与一个派生有关,那么 MobX 将自动地追踪它们。为了测试这一点,只需要尝试改变下面的 input 框中的名字(测试前先确保你点击了 Run Code 按钮!)。

异步操作

由于我们的 Todo 小应用中的所有数据都是派生自 state,因此 state 何时改变并不重要。这使得创建异步操作变得异常简单。点击下面的按钮(多次)以模拟异步地创建新的待办项。


Load todo


后面的代码给常简单。我们首先通过更新 pendingRequests 这一 store 属性使 UI 显示当前的加载状态。当加载结束之后,沃恩跟新 store 中的待办项并再次减少 pendingRequests 计数。将这段代码与上面的 TodoList 定义相比较以学习如何使用 pendingRequests 属性。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
observableTodoStore.pendingRequests++;
setTimeout(function() {
    observableTodoStore.addTodo('Random Todo ' + Math.random());
    observableTodoStore.pendingRequests--;
}, 2000);

开发工具

mobx-react-devtools 包提供了一个被用于 MobX + ReactJS 应用的显示在屏幕右上角的开发工具。点击第一个按钮将会高亮每一个被重新渲染的 @observer 组件。如果你点击第二个按钮,预览中的组件依赖树将会显示出来,你可以在任何时候准确地检测出它正在观察的是哪一段数据。

结论

就这么多!没有样板。只有一些简单的声明式组件用来形成我们整体的 UI。这份 UI 完全响应式地派生自我们的 state。你现在可以开始在你的应用中使用 mobxmobx-react 包啦。下面对你目前学到的东西做一个简要总结:

  1. 使用 @observable 装饰器或 observable(objectorarray) 函数使 MobX 可以追踪对象。
  2. @computed 装饰器可被用于创建基于 state 自动计算值的函数。
  3. 使用 autorun 来自动地运行依赖于 observable state 的函数。这对于打日志、发起网络请求等来说很有用。
  4. 使用 mobx-react 包中的 @observer 装饰器将你的 React 组件变得真正的可响应。他们将会自动并有效地更新。即使是在用够大量数据的大型复杂项目中。

多花点时间玩玩上面的可编辑代码块,以对 MobX 如何对你的操作作出响应有一个基本的概念。例如,你可以为 report 函数增加一个 log 语句来看它什么时候被调用;或者完全不要显示 report 来看看会对 TodoList 的渲染造成什么影响;或者在某些情况下不要显示它……

MobX 不是一个 state 容器

人们通常将 MobX 当做是 Redux 的替代。但是请注意,MobX 只是一个解决技术问题的库,其本身并没有 state 容器。从这个意义上说,上面的例子是人为设计的,所以我们建议您使用适当的工程实践,如在方法中封装逻辑、在 store 或控制器中组织它们等等。或者,像 HackerNews 中某位用户说的:

“MobX,它总是被提起,但我忍不住要赞美它。使用 MobX 写东西意味着它可以完成所有控制器(controllers) / 调度员(dispatchers) / 操作(actions) / 管理程序(supervisors) 以及其他管理数据流需要考虑的工作,而不只是完成一个 Todo 应用默认要求完成的那些工作。”


往期精选文章

使用虚拟dom和JavaScript构建完全响应式的UI框架

扩展 Vue 组件

使用Three.js制作酷炫无比的无穷隧道特效

一个治愈JavaScript疲劳的学习计划

全栈工程师技能大全

WEB前端性能优化常见方法

一小时内搭建一个全栈Web应用框架

干货:CSS 专业技巧

四步实现React页面过渡动画效果

让你分分钟理解 JavaScript 闭包



小手一抖,资料全有。长按二维码关注京程一灯,阅读更多技术文章和业界动态。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2017-10-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 京程一灯 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
dart设计模式之装饰器模式
装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。
徐建国
2021/08/07
6770
Java设计模式学习笔记—装饰器模式
文章最后“Java设计模式笔记示例代码整合”为本系列代码整合,所有代码均为个人手打并运行测试,不定期更新。本节内容位于其Decorator包(package)中。
WindCoder
2018/09/19
6580
Java设计模式学习笔记—装饰器模式
JAVA设计模式8:装饰模式,动态地将责任附加到对象上,扩展对象的功能
在 Java 中,装饰模式通过动态地将责任附加到对象上,以扩展其功能,它提供了一种比继承更灵活的方式来扩展对象的功能。
Designer 小郑
2023/09/15
5260
JAVA设计模式8:装饰模式,动态地将责任附加到对象上,扩展对象的功能
十四、装饰器模式 ( Decorator Pattern )
装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构
botkenni
2022/09/07
2630
十四、装饰器模式 ( Decorator Pattern )
装饰器模式:让你的对象变得更强大
在日常开发中,当需要给一个现有类添加附加职责,而又不能采用生成子类的方法进行扩充时。例如,该类被隐藏或者该类是终极类或者采用继承方式会产生大量的子类。这时候,我们该怎么办呢?我们可以使用装饰器器模式来解决这个问题,本文将从以下四个方面讲解装饰器器模式。
wayn
2023/09/06
1820
装饰器模式:让你的对象变得更强大
【Java设计模式】代理模式(Proxy Pattern)
为其他对象提供一种代理,以控制对这个对象的访问,代理对象在客户端和目标对象之间起到中介作用。
JavaEdge
2022/11/30
4150
【Java设计模式】代理模式(Proxy Pattern)
装饰器模式
// Decorateor Pattern // 向一个现有的对象添加新的功能,同时又不改变其结构(方法签名不变)。 // 可动态的给一个对象添加一些额外的职责。比继承创建子类更灵活。 // // Step1: 创建一个接口 public interface Shape { void draw(); } // step2: 创建实现接口的实体类 Rectangle.cs Circle.cs public class Rectangle : Shape { public void draw
用户2434869
2018/09/12
5270
「聊设计模式」之代理模式(Proxy)
🏆本文收录于《聊设计模式》专栏,专门攻坚指数级提升,助你一臂之力,带你早日登顶🚀,欢迎持续关注&&收藏&&订阅!
bug菌
2023/11/11
3420
「聊设计模式」之代理模式(Proxy)
java设计模式
java中一般认为有23种设计模式,当然暂时不需要所有的都会,但是其中常见的几种设计模式应该去掌握。
EdwinYang
2023/10/16
2740
十七、代理模式 ( Proxy Pattern )
在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层
botkenni
2022/09/23
1530
十七、代理模式 ( Proxy Pattern )
Java设计模式实战-代理模式(Proxy Pattern)
为其他对象提供一种代理,以控制对这个对象的访问,代理对象在客户端和目标对象之间起到中介的作用。
JavaEdge
2021/02/23
3510
Java设计模式实战-代理模式(Proxy Pattern)
05.静态代理设计模式
《静态代理设计模式》详细介绍了静态代理的基本概念、原理与实现、应用场景及优缺点。主要内容包括静态代理的由来、定义、使用场景、实现方式、结构图与时序图,以及其在降低耦合、保护对象权限等方面的优势。同时,文章也指出了静态代理的局限性,如缺乏灵活性、难以复用、难以动态添加功能等,并介绍了动态代理如何弥补这些不足。最后,通过多个实际案例和代码示例,帮助读者更好地理解和应用静态代理模式。
杨充
2024/10/24
1000
代理模式 静态代理和动态代理(jdk、cglib)
一个类代表另一个类去完成扩展功能,在主体类的基础上,新增一个代理类,扩展主体类功能,不影响主体,完成额外功能。比如买车票,可以去代理点买,不用去火车站,主要包括静态代理和动态代理两种模式。
小明爱吃火锅
2023/09/17
2440
23种设计模式详解与示例代码(详解附DEMO)
本文将探讨设计模式在Java中的应用与实现。设计模式是一套被广泛接受的解决常见软件设计问题的经典方法。在Java编程中,设计模式是提高代码可读性、可维护性和可扩展性的关键。本文将详细介绍Java中常用的几种设计模式,包括工厂模式、单例模式、观察者模式和装饰器模式,并提供具体的代码示例和解释,帮助读者深入理解和学习这些模式的实现方式。
默 语
2024/11/20
8290
Java 23种设计模式全归纳 | 已打包请带走
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。
Jingbin
2020/02/13
7680
Java 世界的法外狂徒:反射
反射(Reflection)机制是指在运行时动态地获取类的信息以及操作类的成员(字段、方法、构造函数等)的能力。通过反射,我们可以在编译时期未知具体类型的情况下,通过运行时的动态查找和调用。 虽然 Java 是静态的编译型语言,但是反射特性的加入,提供一种直接操作对象外的另一种方式,让 Java 具备的一些灵活性和动态性,我们可以通过本篇文章来详细了解它
phoenix.xiao
2023/08/28
2760
Java 世界的法外狂徒:反射
架构思想--基础架构
  最近研究一些架构思想,看看李云华老师的专栏(从0开始学架构),做些笔记有些个人的思考。
Dlimeng
2023/06/29
2230
架构思想--基础架构
Java一分钟之-设计模式:装饰器模式与代理模式
装饰器模式和代理模式都是在不改变原有对象的基础上,为对象添加新功能的设计模式。在这篇博客中,我们将讨论这两种模式的基本概念、常见问题及如何避免它们,并提供代码示例。
Jimaks
2024/05/28
6710
Java一分钟之-设计模式:装饰器模式与代理模式
Java设计模式(7)装饰器模式
装饰器模式是一种结构型设计模式,用于动态地为对象添加额外的行为或责任,而无需修改其原始类。它允许将对象包装在一系列装饰器中,每个装饰器都添加一些特定的功能,从而实现对对象行为的灵活扩展。
Jensen_97
2024/04/18
1760
Java设计模式(7)装饰器模式
小谈设计模式(8)—代理模式
主要对目前市面上常见的23种设计模式进行逐一分析和总结,希望有兴趣的小伙伴们可以看一下,会持续更新的。希望各位可以监督我,我们一起学习进步,加油,各位。
学编程的小程
2023/10/11
1830
小谈设计模式(8)—代理模式
相关推荐
dart设计模式之装饰器模式
更多 >
LV.0
长亮科技技术经理
目录
  • 核心理念
  • 一个简单的 todo store
  • Becoming reactive
  • 让 React 变得有响应(reactive)
  • 使用引用
  • 异步操作
  • 开发工具
  • 结论
  • MobX 不是一个 state 容器
加入讨论
的问答专区 >
1解决方案专家擅长1个领域
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档