You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
MobX 通过 响应式编程(在命令式编程环境中,a := b + c 表示将表达式的结果赋给 a,而之后改变 b 或者 c 的值不会影响 a 的值,但在响应式编程中, a 的值会随着 b 或 c 的值得改变而改变)的思想来管理数据。MobX 也是支持单向数据流的,是通过 action 触发 state 的变化,进而触发 state 的衍生对象(Computed 和 Reactions)。所有的衍生默认都是同步更新的。
图4 - MobX实现
概念
装饰器
ESNext 中新增了 decorator 属性,所谓装饰器,可以简单的理解为 锦上添花;以钢铁侠为例,钢铁侠本质上是一个人,只是装饰了很多的武器以后才变得很 NB ,不过怎么装饰他还是一个人。
图5 - 钢铁侠装饰器
function decorateArmour(target, key, descriptor) {
const method = descriptor.value;
let moreDef = 100;
let ret;
descriptor.value = (...args)=>{
args[0] += moreDef;
ret = method.apply(target, args);
return ret;
}
return descriptor;
}
class Man{
constructor(def = 2,atk = 3,hp = 3){
this.init(def,atk,hp);
}
@decorateArmour //这个就是使用了装饰器
init(def,atk,hp){
this.def = def; // 防御值
this.atk = atk; // 攻击力
this.hp = hp; // 血量
}
toString(){
return `防御力:${this.def},攻击力:${this.atk},血量:${this.hp}`;
}
}
var tony = new Man();
console.log(`当前状态 ===> ${tony}`);
// 输出:当前状态 ===> 防御力:102,攻击力:3,血量:3
Computed values are values that can be derived from the existing state or other computed values. Conceptually, they are very similar to formulas in spreadsheets. Computed values can't be underestimated, as they help you to make your actual modifiable state as small as possible. Besides that they are highly optimized, so use them wherever possible.
计算值(computed values)是可以根据现有的状态或其它计算值衍生出的值。 概念上来说,它们与excel表格中的公式十分相似。 不要低估计算值,因为它们有助于使实际可修改的状态尽可能的小。 此外计算值还是高度优化过的,所以尽可能的多使用它们。
以上的这句话是 MobX 官网的原话,这段话充分的说明了 MobX 的使用场景和重要性。Mobx 是纯函数,不能改变state的状态,computed value 采用的是延迟更新,computed values are automatically derived from your state if any value that affects them changes。如果一个计算值不再被观察了,例如使用它的UI不复存在了,MobX 可以自动地将其垃圾回收。
However, a nicer approach is to use the built-in concept of flows. They use generators. Which might look scary in the beginning, but it works the same as async / await. Just use function * instead of async and yield instead of await. The advantage of flow is that it is syntactically very close to async / await (with different keywords), and no manually action wrapping is needed for async parts, resulting in very clean code.
flow can be used only as function and not as decorator. flow integrates neatly with MobX development tools, so that it is easy to trace the process of the async function.
然而,更好的方式是使用 flow 的内置概念。它们使用生成器。一开始可能看起来很不适应,但它的工作原理与 async / await 是一样的。只是使用 function * 来代替 async,使用 yield 代替 await 。 使用 flow 的优点是它在语法上基本与 async / await 是相同的 (只是关键字不同),并且不需要手动用 @action 来包装异步代码,这样代码更简洁。
flow 只能作为函数使用,不能作为装饰器使用。 flow 可以很好的与 MobX 开发者工具集成,所以很容易追踪 async 函数的过程
背景
在 2013 年以前,对于数据流的控制一直以来是使用的 MVC(Model-View-Controller) 架构模式来进行数据的管理:
图1-理想的MVC
但是对于非常巨大的代码库和庞大的组织来说,MVC 很快就会变得非常复杂。每当工程师需要新增一个功能的时候,对代码的修改可能带来新的 bug,不同模块之间的依赖关系会变得“脆弱而且不可预测”。如图2:
图2 - 现实的MVC
基于以上的情况,Facebook 公司推出了 Flux 框架,用来管理数据,相比于 MVC ,它是一种更严格的数据流控制。
图3 - Flux框架
一个 Flux 包含四个部分,如下:
当用户请求一个动作,会触发 Action,之后 Action e驱动 Dispatcher 来进行分发 Action 操作,从而更新 Store 中的数据,Store 中数据改变后,就会更新 View 的展示。
Flux 虽然很好,也有不足之处,比如说 难以进行服务端渲染、Store 混杂了逻辑和状态等,但是这种 单一数据流 的理念衍生出了像 Redux 和 MobX 框架的实现。本篇文章着重讲述 MobX。
简介
MobX 通过 响应式编程(在命令式编程环境中,
a := b + c
表示将表达式的结果赋给 a,而之后改变 b 或者 c 的值不会影响 a 的值,但在响应式编程中, a 的值会随着 b 或 c 的值得改变而改变)的思想来管理数据。MobX 也是支持单向数据流的,是通过 action 触发 state 的变化,进而触发 state 的衍生对象(Computed 和 Reactions)。所有的衍生默认都是同步更新的。图4 - MobX实现
概念
装饰器
ESNext 中新增了 decorator 属性,所谓装饰器,可以简单的理解为 锦上添花;以钢铁侠为例,钢铁侠本质上是一个人,只是装饰了很多的武器以后才变得很 NB ,不过怎么装饰他还是一个人。
图5 - 钢铁侠装饰器
tips:
ES7 中的 decorator 其实是一个语法糖,不过依赖于
Object.defineProperty(obj, prop, descriptor)
可观察数据
在 MobX 中, State 就对应业务的原始状态,可以通过
observable
或者@observable
将这些状态变为可观察的,顾名思义,可观察数据就是 当数据变化的时候,可以被观察到。一般来说,原始类型(String, Number, Boolean, Symbol),对象(objects),数组(arrays),maps 都可以被观察,其中
objects
arrays
maps
用observable
转换为可观察数据,而 原始数据 用observable.box
转化为可观察数据;普通object
普通对象和非普通对象的划分就是看对象是否有原型,如果没有原型或者原型是Object.prototype 的对象,那么就是普通对象;
tips: 对于新增的属性,不可以被观察,如果需要被观察需要用
extendObservable
或者set
;observable 会递归遍历整个对象,即使这个属性还是个对象;非普通对象
observable 会返回一个特殊的boxed values 类型的可观测的对象,返回的 boxed values 对象并不会把非普通对象的属性转换为可观测的,而是保存一个指向对象的引用;这个引用是可观测的;对原对象的访问和修改可以通过
get()
和set()
方法操作。对于非普通的对象的属性,可以通过以下的方式将其变为可观察的:
这种方式比较麻烦,所以推荐使用装饰器的方式,这种方式的好处还在于对于原始数据类型的数据的话,自己内部有判断,不用使用
observable.box()
如下:arrays
tips: 判断是不是数数组的两种方式:
Array.isArray(observable([]).slice())
和isArrayLike(arr)
;maps
tips: Map 对象的每个对象都是可观测的,而且向Map对象中添加或删除元素的行为也是可以被观测的;
原始数据类型
对于原始数据的话,通过
get()
获取数据,通过set()
设置数据;对 observables 做出响应
MobX 中四种方式对 observables 做出响应,分别为
@computed
autorun
when
reaction
,接下来会分别介绍这四种方式的使用场景:@computed
以上的这句话是 MobX 官网的原话,这段话充分的说明了 MobX 的使用场景和重要性。Mobx 是纯函数,不能改变state的状态,computed value 采用的是延迟更新,computed values are automatically derived from your state if any value that affects them changes。如果一个计算值不再被观察了,例如使用它的UI不复存在了,MobX 可以自动地将其垃圾回收。
autorun
顾名思义,就是自动执行,当使用 autorun 时,所提供的函数总是立即被触发一次,然后每次它的依赖关系改变时会再次被触发。相比于 computed,他不会产生一个新的值,它更像是发起请求这样的命令式副作用;它会返回一个清楚函数 disposer,当不需要观察相关的 state 变化的时,可以调用 disposer 函数清除副作用。
when
when(predicate: () => boolean, effect?: () => void, options?)
,predicate 会自动响应它使用的任何的state
的变化,当predicate
返回ture 的时候,函数effect会执行,且执行一次。when
也返回一个disposer
函数。when 非常的适合用在以影响式的方式执行取消或者清楚逻辑的场景;componentDidMount() {
class showDetail{
@observable first = 2;
@observable second = 3;
reaction(() => data, (data, reaction) => { sideEffect }, options?)
,它接收两个函数参数,第一个(数据函数)是用来追踪并返回数据作为第二个函数(效果函数)的输入,第二个函数 reaction 会返回一个清楚函数 disposer。第一个函数是返回需要被观察的数据,第二个函数接收这个需要被观察的数据,同时传入 reaction,当被观察的数据改变的时候,就会触发 reaction,这样就不像autorun
似的,当一个状态改变的时候就会触发,从而建立和要被观察的数据和reaction之间的关系;总结来说,相较于autorun,reaction 可以对跟踪哪些对象有更多的控制;改变observables
官方建议修改 observables 或者具有副作用的函数使用
@action
,简单的说就是对于修改可观察的数据,建议使用@action
。@action 和 @action.bound
asny actions
使用
action
这里列出了比较常见的一种方式,就是使用
action
进行包装:使用
runInAction
工具函数这种模式的优点在于你可以不用到处的写
action
,而仅仅在整个过程结束的时候对状态进行修改:flows
mobx-react
mobx-react
顾名思义,是联系Mobx
和React
之间的桥梁,从而更方便的使用Mobx
在React
中开发,经常的用到有:Provider
inject
observer/@observer
:Provider
Provider 是一个 React 组件,利用 Reacxt 的
context
机制把应用所需的state
传递给子组件。inject
inject 是个高阶组件,和
Provider
结合使用,用于从Provider
提取所需的state
,作为props
传递给目标组件。observer/@observer
手写一个 todoList
开发环境的搭建
MobX 中大量的使用了 ES.Next 中的装饰器语法,为了在新搭建的项目中支持这种语法,有两种实现方式:
使用
create-react-app project-name --scripts-version custome-react-scripts
创建项目,这种方式创建的项目,支持 修饰器语法、Less、Sass;仍然使用
create-react-app project-name
创建项目,然后执行yarn run eject
弹射出配置文件,然后安装yarn add babel-plugin-transform-decorators-legacy -D
修改 webpack 的配置文件,添加以上的内容配置好了以后,还要通过
yarn add mobx-react mobx -S
安装mobx-react
;目录结构
图6-目录结构
代码
因为代码比较多,所以直接上github的地址:https://github.com/ycshill/shared/tree/master/mobx-share
MobX 常用工具函数和调试
toJS(value, options)
递归地将一个observable对象转换为javascript结构。支持observable数组、对象、映射和原始数据类型
mobx-react-devtools
mobx-react-devtools 是一个用来调试 MobX + React 项目的工具,可以追踪组件的渲染以及组件依赖的可观测数据。
安装
yarn add mobx-react-devtools -D
代码
性能优化
尽可能多地使用小组件
@observer 组件会追踪render方法中所有的可观测的值,当任何一个值变化的时候,都会重新渲染,所以组件越小,重新渲染的变化就越小。
在专用的组件中渲染列表
晚一点使用间接引用值
快的:
<DisplayName person={person} />
慢的:
<DisplayName name={person.name} />
Redux VS Mobx
图8- redux&mobx
store
,通过拆分reducer
来拆分应用逻辑,单一store
可以方便不同组件之间进行的数据共享;store
,把逻辑拆分到不同的store
中,当维护多个组件之间的数据共享、相互之间的引用的时候会变得特别的麻烦。state
不可改变,每次状态的变化,都会创建一个新的state
state
是可观测对象,并且state
可以被直接的修改,state
的变化会自动触发使用它的组件重新渲染。推荐一篇文章:我为什么从Redux迁移到了Mobx
@observer
修饰组件就可在修改数据的时候自动进行处理更新,同时免面向对象的编程的写法感觉如果熟悉面向对象编程的开发人员,会减少学习的成本;store
对于数据的管理,更加容易的跟踪state
,但是学些成本和负责的更改数据流程让人觉得不是很友好。The text was updated successfully, but these errors were encountered: