observable的实现机制
mobx的observable能够实现数据的观测,那内部是怎么实现数据的观测的呢
import {observable} from './mobx'
let dl = observable({name:'lrm'});
console.log(dl);
这里我手动创建了一个react的工程
手动引入自己创建的mobx的index文件,引入创建的observable文件
function deepProxy(value,handler) {
if(typeof value !== 'object') return value;
//如果是对象走代理,代理的第一个是值,第二个是对象
return new Proxy(value,handler());
}
function createObservable(val){
//声明一个专门用来代理的代理对象
let handler=()=>{
return {
set(target,key,value){
return Reflect.set(target,key,value)
},
get(target,key){
return Reflect.get(target,key)
}
}
}
return deepProxy(val,handler)
}
const observable=(target)=>{
return createObservable(target)
}
export default observable;
可以实现同样的效果
import {observable} from './mobx'
let dl = observable({name:'lrm',age:{num:18}}});
console.log(dl);
这时候我们发现如果对象只是一层可以实现,但是如果是多层就没有代理了,那该如何改进:
针对多层次的代理可以进行遍历再代理
function deepProxy(value,handler) {
if(typeof value !== 'object') return value;
for(let key in value) {
value[key] = deepProxy(value[key],handler);
}
//如果是对象走代理,代理的第一个是值,第二个是对象
return new Proxy(value,handler);
}
function createObservable(val){
//声明一个专门用来代理的代理对象
let handler=()=>{
return {
set(target,key,value){
return Reflect.set(target,key,value)
},
get(target,key){
return Reflect.get(target,key)
}
}
}
return deepProxy(val,handler)
}
const observable=(target)=>{
return createObservable(target)
}
export default observable;//对多层次的代理,需要遍历代理
autorun:当使用autorun时,所提供的函数总是立即被触发一次,然后每次它的依赖关系改变时候会再次被自动触发
import {observable,autorun} from './mobx'
let dl = observable({name:'lrm',age:{num:18}});
console.log(dl.age);
autorun(()=>{
console.log(dl.age.num);
})
这时候拿到的值是18,如果我想改变ad.age.num,不会直接触发改变,需要做一个依赖收集,触发改变。
比如我在autorun后面加上dl.age.num = 100;,想让antorun打印的值是18,100怎么办
在 aotorun中 reaction相当于一个中间的垫片,执行reaction.end()方法是为了清空,防止下一次使用值被污染。
import Reaction from './reaction';
let autorun = (handler)=>{
Reaction.start(handler);//先保存了这个函数
handler(); //调用这个方法触发get属性
Reaction.end();
}
export default autorun;
在reaction.js中的实现思路如下:
let nowFn = null; //当前的autorun方法
let couter = 0;//为了保证new的实利是唯一的
class Reaction{
constructor(){
this.id= ++couter;
this.store={};//存储当前可观察对象nowFn{id:[nowFn]}
}
run(){
//拿到里面的属性让跑一遍,让依赖的值执行
if(this.store[this.id]){
this.store[this.id].forEach(w=>{
w();
})
}
}
collect(){
//当前有需要绑定的函数才进行绑定,没有先不理
if(nowFn){
this.store[this.id] = this.store[this.id] ||[];
//调用的时候做一个对应关系
this.store[this.id].push(nowFn);
}
}
static start(handler){
nowFn = handler;
}
static end(){
nowFn = null;
}
}
export default Reaction;
在autorun文件中引入start和end方法,获取改变之后的值是在observa的get中进行获取:
function createObservable(val){
//声明一个专门用来代理的代理对象
let handler=()=>{
let reaction = new Reaction();
return {
get(target,key){
reaction.collect();
return Reflect.get(target,key)
},
set(target,key,value){
let r = Reflect.set(target,key,value);
reaction.run();
return r;
},
}
}
return deepProxy(val,handler)
}
const observable=(target)=>{
return createObservable(target)
}
这样我们在get时候收集,set时候执行:
在index文件中再次执行:
import {observable,autorun} from './mobx'
let dl = observable({name:'lrm',age:{num:18}});
console.log(dl.age);
autorun(()=>{
console.log(dl.age.num);
})
dl.age.num = 100;
这时候在浏览器的运行结果是: