Mobx新手入门学习

Mobx 简单可扩展的状态管理

目录

10.1 通过和Redux比较对Mobx介绍 2

  1. Mobx对比Redux 2
  2. 单向数据流上的对比 3
  3. 装饰器语法 3
    10.2 Mobx包含的主要概念 3
  4. state介绍 3
    ①普通对象(Plain Object) 4
    ②ES 6 Map 5
    ③数组 5
    ④非普通对象 5
    ⑤基本数据类型 6
  5. computed valued 6
  6. reaction 6
    ①autorun 7
    ②reaction 7
    ③when 8
    ④三种方式使用的场景 8
    4.action 9
    10.3 Mobx响应的常见误区 9
    ① Mobx是通过追踪属性的访问来追踪值的变化,而不是直接追踪值本身的变化 9
    10.4 在React中使用Mobx 10

10.1 通过和Redux比较对Mobx介绍

  1. Mobx对比Redux
    ①开发难度低:redux的API是函数风格,原理和思想很简单,但是对于新手不容易使用。而mobx采用语义更丰富的响应式风格(是一种面向数据流和变化传播的编程方式),对面向对象匹配更简略的API方法,集成度比redux更高,减少引入第三方库。
    ②开发代码少:redux引入了action、reducer、store,每增加一个状态都需要更新这些位置,而mobx只需要在store中更改即可。
    ③渲染能力好:redux组件中编写ShouldComponentUpdate方法可以避免不必要的重渲染,提升页面性能。但数据层次很复杂时,实现起来会很麻烦。mobx能精确哪些组件重渲染,哪些不渲染,通过合理的组织组件层级和数据结构位置,可以轻易地将组件重渲染限制到最小范围内。
    2.单向数据流上的对比
    ①redux的单向数据流:

②mobx的单向数据流:

3.装饰器语法
为了引入装饰器语法,之后我们在创建项目的时候,都用此命令创建:
create-react-app 项目名 --scripts-version custom-react-scripts

我们这里用到的语法修饰器就是在’mobx’、’mobx-react’等其他一些包提供的API或组件前加上@,使其具有语法修饰器的功能。
常用的语法修饰器有: observable、@observable、@computed(get+方法名)、observer、@observer、action、@action、@inject
具体的使用方法在10.2节分类进行讲解

10.2 Mobx包含的主要概念

  1. state介绍
    State是驱动应用的数据,是应用的核心。在实际使用中,一般会创建一个store来管理state;在Mobx中可以使用一个应用中使用多个store(项目中会创建一个store文件,该文件下存放不同类需求的不同state,如需使用具体的store,只需要引入相关的文件,通过语法修饰器@inject(“store”),inject从context中取出store对象,注入到组件的props中)。另外Mobx的state的结构不需要做标准化处理,可以有多层嵌套结构(Redux需要做扁平化处理)。

①普通对象(Plain Object)
普通对象指原型不存在或原型是Object.prototype的对象,可以对比后面的非普通概念进行理解。Mobx根据普通对象创建一个可观测的新对象,新对象的属性和普通对象相同,但每一个都是可观测的
import { observable,autorun } from “mobx”;
let person = observable({ //此处使用observable将属性定义为可观测
name: “Jack”,
age: 20
});
// mobx.autorun会创建一个reaction自动响应的state的变化,后面会讲到
autorun(()=>
console.log(‘name: p e r s o n . n a m e , a g e : {person.name},age: person.name,age:{person.age}’)
);
person.name = “Tom”; //输出:name:Tom,age:20
person.age = 25; //输出: name:Tom,age:20
我们用语法修饰器修饰过后的@observable来改写上面的代码
import { observable,autorun } from “mobx”;
class Person {
@observable name = “Jack”;
@observable age = 20;
}
const person = new Person();
autorun(()=>
console.log(‘name: p e r s o n . n a m e , a g e : {person.name},age: person.name,age:{person.age}’)
);
person.name = “Tom”; //输出:name:Tom,age:20
person.age = 25; //输出: name:Tom,age:20
注意几个问题:
a.只有当前普通对象已经存在的属性才会转换为可观测的,后面添加的新属性都不会自动变为可观测的。
b.属性的getter会自动转换成computed value,效果和使用@computed相同,这里我们同意使用@computed get+方法名。
c.observable会递归地便利整个对象,每当遇到对象的属性值还是一个对象时(不包含非普通对象),这个属性值将会继续被observable转换
②ES 6 Map
返回一个新的可观测的Map对象。Map对象的每一个对象都是可观测的,而且向Map
对象中添加或删除新元素的行为也是可观测的,这是Map类型的可观测state最大的特点。
import { observable,autorun } from “mobx”;
// Map可以接收一个数组作为参数,数组的每一个元素代表Map对象中的一个键值对
const map = new Map([[“name”,“Jack”],[“age”,20]]);
const person = observable(map); //person是一个可观测的Map对象
autorun(()=>
console.log(‘name: p e r s o n . g e t ( " n a m e " ) , a g e : {person.get("name")},age: person.get("name"),age:{person.get(“age”),address:${person.get(“address”)}’)
);
person.set(“address”,“Shanghai”); // 输出:name:Jack,age:20,address:Shanghai

③数组
返回一个新的可观测数组。数组元素的增加或减少都会被自动观测。
const todos = observable([“Learn React”,“Learn Redux”]);
把数组todos定义为可观测的数组(省略autorun)
todos.push(“Learn Mobx”);//在数组末尾添加一个或多个元素,并返回数组新长度
todos.shift();//把数组第一个元素删除,并返回数组新长度

④非普通对象
非普通对象特指以自定义函数作为构造函数创建的对象。observable会返回一个特殊的boxed values类型的可观测对象。返回的boxed values对象并不会把非普通对象的属性转换成可观测的,而是保存一个指向原对象的引用,这个引用是可观测的。对原对象的访问和修改需要通过新对象的get()和set()方法操作。(我们同一用装饰器语法,这里就不再写出例子)
将非普通对象的属性转换成可观测的是自定义构造函数的责任。使用装饰器@observable的方式:
import { observable,autorun } from “mobx”;
class Person {
@observable name;
@observable age;
constructor(name,age){
this.name = name;
this.age =age
}
}
var person = new Person(“Jack”,20);
autorun(()=>console.log(‘name: p e r s o n . n a m e , a g e : {person.name},age: person.name,age:{person.age}’));
person.age = 25;
⑤基本数据类型
Mobx是将包含值的属性(引用)转换成可观测的,而不是直接把值转换成可观测的,当observable接收的参数是JavaScript的基本数据类型时,Mobx不会把它们转换成可观测的,而是同处理非普通对象一样,返回一个boxed values类型的对象。
2.computed valued
computed valued是根据state衍生出来的新值,新值必须是通过纯函数计算得到的。
当computed value有被reaction使用时,computed value才会自动更新。
一般通过computed和@computed创建computed value
举一个@computed的例子:
import { observable,computed,autorun } from ‘mobx’;
class Person {
@observable name;
@observable age;
@computed get isYoung(){ //使用@computed装饰器创建computed value
return this.age < 25;
}
constructor(name,age){
this.name = name;
this.age = age
}
}
const person = new Person(“Jack”,20);
//使用autorun创建reaction响应,这里用到了isYoung所以@computed自动更新
autorun(()=>console.log(‘name: p e r s o n . n a m e , i s Y o u n g : {person.name},isYoung: person.name,isYoung:{person.isYoung}’ ));
person.age = 25; //输出:name:Jack,isYoung:false
3.reaction
除了observer/@observer外,常用的创建reaction的API有autorun、reaction、when,这几个API直接作用与函数而不是组件。
Observer/@observer是mobx-react这个包提供的API,常用的使用方式有三种:
observable((props,context)=>ReactElement)
observable(class MyComponent extends React.Component{…})
@observable class MyComponent extends React.Component{…}

简单的说,reaction产生的不是一个值,而是执行一些有副作用的动作,例如打印信息到控制台(前面用到的autorun都是console.log)、发送网络请求、根据React组件树更新DOM(本章给的源代码中使用@observer将render组件封装成reaction,使用observer将要渲染的TodoView封装成reation)等

①autorun
autorun在前面已多次使用,这里就不再多做介绍,注意一点:autorun会返回一个清除函数disposer,当不需要观察相关state的变化时,可以调用disposer函数清除副作用
disposer();//清除autorun
②reaction
用法:reaction( () => data , data => { sideEffect } , option?)
它接收两个参数,第一个函数返回被观测的state,这个返回值同时是第二个函数的输入值,只有第一个函数的返回值发生变化时,第二个函数才会执行,第三个参数option是可选参数,一般很少用到,用到时我们在研究该怎么使用。注意:reaction也会返回一个清除函数disposer。
课堂上我们还遗留一个问题就是reaction1和reaction2的输出顺序,看了一下mobx官网提供的例子,可以认为本书作者先输出reaction2再输出reaction1就是先展示了正确的使用方法,再展示了错误的使用方法

③when
用法:when( () = > condition, () =>{ sideEffect })
condition会自动响应它使用的任何state变化,当condition返回true时,函数sideEffect会执行一次,且只执行一次。
④三种方式使用的场景
当你想创建一个响应式函数,而该函数本身永远不会有观察者时,可以使用mobx.autorun
相比于reaction可以对跟踪哪些对象有更多的控制。
而when非常适合用在以响应式的方式执行取消或清除逻辑的场景。
4.action
action是用来改变state的函数。Mobx提供了API action和@action用来包装action函数
action( fn )
@action classMethod
当函数内多次修改state时,action/@action会执行批处理操作,只有所有的修改都执行完成后,才会通知相关的computed value和reaction
使用action要注意函数内this指向的问题
class Ticker {
@observable tick = 0;
@action
increment(){
this.tick++;//这里的this并不是期待的Ticker,而是指向全局的window对象
}
}

可以使箭头函数解决this绑定的问题:
class Ticker {
@observable tick = 0;
@action
increment=()=>{ //箭头函数
this.tick++;
}
}
如果你不想使用箭头函数的话,还可以使用Mobx提供的@action.bound和action.bound两个API帮助完成this绑定的工作:
@action.bound
increment(){
this.tick++;
}
10.3 Mobx响应的常见误区
① Mobx是通过追踪属性的访问来追踪值的变化,而不是直接追踪值本身的变化
必须在Mobx的computed value和reaction中解引用可观测对象的属性,才能正确观测到这些属性值的变化。
②Mobx只追踪同步执行过程中的数据。
异步执行(有时间延时)autorun是不会有响应的
③observer创建的组件,只有在当前组件render方法中直接使用的数据才会被追踪
const MyComponent = observer(({ todo }) =>

{todo.title}
}/>
)
这个例子中,使用SomeContainer标签,标签里面用到了title回调函数,回调函数执行时,todo.title才会响应。
这里我们做出改变(这里也很好理解:TitleRenderer是一个可观测组件,SomeContainer通过使用TitleRenderer响应title的变化):
const MyComponent = observer({ todo }) =>
}/>
)
const TitleRenderer = observer(({ todo })=>

{ todo.title }
); 10.4 在React中使用Mobx 我们这里再次用到mobx-react包提供的另外两个常用的API ①Provider Provider是一个React组件,利用React的context机制把应用所需的state传递给子组件 ②inject Inject是一个高阶组件,它和Provider结合使用来渲染根节点。用于从Provider提供的state中选取所需数据,作为props传递给目标组件。 我们用以下方式 @observer @inject("store1","store2")

在解释一下inject
我们这里先构造store及其初始值
const todos = observable([]);
todos.push({ id:1,title:“Task1” });
todos.push({ id:2,title:“Task2” });
然后在MyComponent外部先用@observer封装成reaction,然后再使用@inject(“store”);
inject从context中取出store对象,注入到组件的props中。

你可能感兴趣的:(前端)