用MobX管理状态(ES5实例描述)-2.可观察的类型

2020-06-15 14:59:57 浏览数 (1)

MobX是一个简单有效的状态管理库,以派生(derive)的概念为核心,以观察者模式为手段,达到了修改数据自动更新界面等目的

  • 正因为其本身提供了包装react的方法,可以简洁的改善react组件,所以官网文档和几乎所有教程都以react和ES7的装饰修饰符等特性为切入点
  • 但MobX在传统的ES5环境中也能良好工作,本文尝试以此为出发点,探讨在既有的非react项目中直接引入MobX并用其整理重构老代码的方法

没有babel、webpack、JSX...那么多的套路!直接上手先,走你~

[II]. 可观察的类型

语法 mobx.observable(value)

2.1 普通对象

  • 普通对象指不是通过构造函数创建的,没有特定原型对象的 plain object
  • 如果一个普通对象被传递到 observable() 中,其所有属性都会成为可观察的,并被拷贝到一个副本中(对副本的更改也同时影响原始对象的值)
  • 默认是递归处理的,如果一个属性是对象或数组,其元素也会被观察
代码语言:javascript复制
var $ctn = document.querySelector('#container');var obj = {
   a: 1,
   b: {
       c: 3,
       d: 4
   }
};var observ = mobx.observable(obj);
   
setTimeout(function(){
   observ.b.c = 666;
}, 1000);mobx.autorun(function(){
   //元素内容变为 [new value] c: 666
   $ctn.innerHTML = "[new value] c: "   observ.b.c;
});setTimeout(function(){
   alert([obj.b.c, observ.b.c]; //666, 666
}, 2000);
浅观察:

observable.shallowObject(value)方法可以实现“浅观察”,只自动响应“浅层”的子属性

代码语言:javascript复制
var $ctn1 = document.querySelector('#container1');
var $ctn2 = document.querySelector('#container2');
var $info = document.querySelector('#info');var obj = {
   a: 1,
   b: {
       c: 3,
       d: 4
   }
};var observ1 = mobx.observable.shallowObject(obj);
var observ2 = mobx.observable.shallowObject(obj);mobx.autorun(function(){
   //元素内容变为 [new value of observ1] c: 3
   $ctn1.innerHTML = "[new value of observ1] c: "   observ1.b.c;
});
mobx.autorun(function(){
   //元素内容变为 [new value of observ2] c: 999, a: 555
   $ctn2.innerHTML = "[new value of observ2] c: "   observ2.b.c   ", a: "   observ2.a;
});setTimeout(function(){
   observ1.b.c = 666;
   observ2.b.c = 999;
   observ2.a = 555;
}, 1000);
setTimeout(function(){
   $info.innerHTML = [obj.b.c, observ1.b.c, observ2.b.c].join(','); //999, 999, 999
}, 2000);

2.2 数组

  • 和对象类似的是,向observable()传递一个数组参数,数组中的每一项也会变为可观察的,且默认为递归处理的深度观察
  • 和对象类似,数组也有一个浅观察的方法 observable.shallowArray(value)
  • Array.isArray(observable([]))会返回fasle,但可用Array.isArray(observable([]).slice())达到正确的效果
  • 与原生数组对象的sort()reverse()方法不同的是,可观察数组的这两个方法返回相应结果的一个数组副本,而不影响原数组
  • 除了内建的数组方法,可观察数组也扩展了如下方法:
    • clear()
    • replace(newItems)
    • find(predicate: (item, index, array) => boolean, thisArg?, fromIndex?)
    • remove(value)
    • peek(): 和slice()类似,返回一个安全的原生数组
    • intercept(change=> change|null ): 拦截更改,并可指定使用自定义后的更改
    • observe(change=>{}, fireImmediately? = false): 监听更改
代码语言:javascript复制
var todos = observable([
   { title: "Spoil tea", completed: true },
   { title: "Make coffee", completed: false }
]);autorun(() => {
   console.log("Remaining:", todos
       .filter(todo => !todo.completed)
       .map(todo => todo.title)
       .join(", ")
   );
});
// Prints: 'Remaining: Make coffee'todos[0].completed = false;
// Prints: 'Remaining: Spoil tea, Make coffee'todos[2] = { title: 'Take a nap', completed: false };
// Prints: 'Remaining: Spoil tea, Make coffee, Take a nap'todos.shift();
// Prints: 'Remaining: Make coffee, Take a nap'

2.3 Map

  • observable.map(values?) 可以创建一个可观察的Map类型
  • 可选的一个参数,可以是一个对象、一个ES6 Map,或是一个键值字符串数组
  • 类似于对象,可以用observable.shallowMap(values)实现浅观察
代码语言:javascript复制
var a = mobx.observable.map({a:111})
console.log('map1 ', a.get('a')); //111var es6Map = new Map();
es6Map.set('a', 222);
var b = mobx.observable.map(es6Map);
console.log('map2', b.get('a')); //222var c = mobx.observable.map(['a', 'b', 'c']);
console.log(c.toJS()); //{a: undefined, b: undefined, c: undefined}

和ES6规范中相同的方法包括:

  • has(key)
  • set(key, value)
  • delete(key)
  • get(key)
  • keys()
  • values()
  • entries()
  • forEach(callback:(value, key, map) => void, thisArg?)
  • clear()
  • size

不同于ES6规范的方法包括:

  • toJS() - 得到一个浅复制的javascript对象( 深复制用mobx.toJS(map) )
  • merge(values) - 合并新的对象到Map中,参数格式同初始化方法
  • replace(values) - 替换,相当于 map.clear().merge(values)
  • intercept(interceptor)
  • observe(listener, fireImmediately?)

2.4 基本类型值和引用

  • 所有JS的基本值都是不可变的,因此单个变量无法被观察
  • MobX将这些类型转换成可观察的“boxed value”

转换后的对象可调用如下方法:

  • get() - 取得当前值
  • set(value) - 替换当前值,并通知所有观察者方法
  • intercept(interceptor)
  • observe(callback: (change) => void, fireImmediately = false)
代码语言:javascript复制
const cityName = observable("Vienna");console.log(cityName.get());
// prints 'Vienna'cityName.observe(function(change) {
   console.log(change.oldValue, "->", change.newValue);
});cityName.set("Amsterdam");
// prints 'Vienna -> Amsterdam'
浅观察
  • observable.shallowBox(value)基于observable.ref()实现了浅观察
  • 这意味着只观察引用本身,而其值并不会被自动观察
代码语言:javascript复制
var str2 = "world";
var pobj2 = mobx.observable(str2);var str3 = "!!!";
var pobj3 = mobx.observable.shallowBox(str3);mobx.autorun(function(){
   console.log(str2, pobj2.get());
   console.log(str3, pobj3.get());
});setTimeout(function() {
   console.log('[after 1s]');
   mobx.runInAction(()=>{
       pobj2.set("wo____rld");
       pobj3.set("~~~");
   });
}, 1000);setTimeout(function() {
   console.log('[after 2s]');
   mobx.runInAction(()=>{
       pobj2.set({a: 11});
       pobj3.set({b: 22});
   });
}, 2000);/* autorun输出:world world
!!! !!![after 1s]
world wo____rld
!!! ~~~[after 2s]
world {$mobx: ObservableObjectAdministration}
!!! {b: 22}
*/

2.5 类实例

对于类实例,需要在构造函数中或对实例对象调用mobx.extendObservable(targetName, ...props)方法:

代码语言:javascript复制
var Person = function(firstName, lastName) {
   //不需要观察的实例属性
   this.id = (new Date).getTime();
   //需要观察的实例属性
   mobx.extendObservable(this, {
       firstName: firstName,
       lastName: lastName
   });
}var matthew = new Person("Matthew", "Henry");//对已初始化的实例增添新的可观察属性
mobx.extendObservable(matthew, {
   age: 25
});
类实例中的描述符
  • 描述符被用来对指定的属性定义特殊的行为
  • 比如用observable.ref()来浅观察引用、用computed()来声明一个派生属性,或用action()定义一个改变状态的动作
代码语言:javascript复制
var Person2 = function(firstName, lastName) {
   this.id = (new Date).getTime();
   mobx.extendObservable(this, {
       firstName: mobx.observable.ref(firstName),
       lastName: mobx.observable.ref(lastName),
       fullName: mobx.computed(function() {
           return this.firstName   " "   this.lastName
       }),
       setLastName: mobx.action(function(name) {
           this.lastName = name;  
       })
   });
};var p2 = new Person2('tom', 'jerry');
p2.setLastName('trump');
p2.firstName = 'donald';
console.log(p2.fullName); //用computed定义的派生属性用法上类似getter
类实例中的 getter/setter
  • 也可以用getter定义一个派生属性
  • 配对的setter是可选的,用来定义一个action; 且该方法不能直接操作派生属性,而是通过改变核心状态影响它
代码语言:javascript复制
var Person3 = function(firstName, lastName) {
   this.id = (new Date).getTime();
   mobx.extendObservable(this, {
       firstName: firstName,
       lastName: lastName,
       get fullName() {
           return this.firstName   " "   this.lastName
       },
       set fullName(newValue) {
           var parts = newValue.split(" ")
           this.firstName = parts[0]
           this.lastName = parts[1]
       }
   });
};var p3 = new Person3;
p3.fullName = "ivanka trump";
console.log(p3.fullName, p3.firstName);
类实例中的浅观察
  • extendShallowObservable(value)同样基于observable.ref()实现了浅观察
  • observable.deep(prop)被用来对某个属性单独指定深观察

0 人点赞