MobX是一个简单有效的状态管理库,以派生(derive)的概念为核心,以观察者模式为手段,达到了修改数据自动更新界面等目的
- 正因为其本身提供了包装react的方法,可以简洁的改善react组件,所以官网文档和几乎所有教程都以react和ES7的装饰修饰符等特性为切入点
- 但MobX在传统的ES5环境中也能良好工作,本文尝试以此为出发点,探讨在既有的非react项目中直接引入MobX并用其整理重构老代码的方法
没有babel、webpack、JSX...那么多的套路!直接上手先,走你~
[II]. 可观察的类型
语法
mobx.observable(value)
2.1 普通对象
- 普通对象指不是通过构造函数创建的,没有特定原型对象的 plain object
- 如果一个普通对象被传递到
observable()
中,其所有属性都会成为可观察的,并被拷贝到一个副本中(对副本的更改也同时影响原始对象的值) - 默认是递归处理的,如果一个属性是对象或数组,其元素也会被观察
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)
方法可以实现“浅观察”,只自动响应“浅层”的子属性
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)
: 监听更改
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)
实现浅观察
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)
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()
实现了浅观察- 这意味着只观察引用本身,而其值并不会被自动观察
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)
方法:
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()
定义一个改变状态的动作
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; 且该方法不能直接操作派生属性,而是通过改变核心状态影响它
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)
被用来对某个属性单独指定深观察