一、绪论
相信大多数前端开发者在找工作的时候都会问到vue的双向数据的绑定原理
很多人都会说:vue内部使用es5的方法 Object.defineProperty()的方法进行属性拦截,把data对象的每个数据的读写通过调用getter和setter方法,当数据发生改变时,通知视图更新更新。
显然这个是知道的只是大概
2、分析mvvm
所谓的 mvvm双向数据绑定,就是通过数据改变通知视图更新,视图变化更新数据
两个问题
1)数据变成可以观测的
2)把对象的每一个属性都变得可观测
3)数据变化怎么更新视图
4)视图变化怎么更新数据
二、问题一:解决怎么使数据变得可以观测、
监测数据对象的变化
es5中的Object.defineProperty()方法 会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性并返回这个对象
代码说话
const shop={
“produceName”:‘华为’,
“price”:10000
}
上述对象我们
可以读 shop.produceName
可以写 shop.produceName=‘苹果’
问题来了:但是我们并不知道它 什么时候读的,什么时候写的
通过es5的Object.defineProperty()进行改写
const shop={}
const price=10000
Object.defineproPerty( shop ,'price',{
get(){
console.log('该方法是当price被读取的时候触发')
return val //把price对应的值return出来 1000
},
set(newVal){
console.log('price数据发生变化时执行')
val=newVal //当修改了老的price的val时 再次打印price 就是新的数据
}
})
//上述是通过 Object.defineProperty()方法 给shop的空对象 定义了一个price属性,并把这个属性的读和写 挂在到了 对象中的get()和set()方法中进行拦截,每当你操作数据的时候就会触发 这两个函数
问题一已决定
三、解决问题二:把对象的每一个属性都变成可观测的数据
function observable(obj){
if(!obj ||typeof obj!=='object'){
//如果obj不存在 或者 obj的类型不等于 object
return;
}
const keys=Object.keys(obj) //获取到所有的key值 返回key组成的数组
keys.forEach((key)=>{
//把每一项都变成可观测的属性
defineReactive(obj,key,obj[key])
})
return obj
}
//现在定义一个可观测的对象
const obj=observable({
name:'小米',
age:23
})
obj 的两个属性都是可观测的了
将一个对象变成可观测的对象
//obj 对象
//key 对象的ket
//val 对象的val
function defineReactive(obj,key,val){
Object.defineProperty(obj,key,{
get(val){
console.log('属性被读取了')
return val
},
set(newval){
console.log('属性被修改了')
var=newval
}
})
}
四、通过依赖 创建专门收集改变的数据的容器
数据的可观测完成之后,我们要收集已经改变的数据,通知视图更新
其实发布订阅者模式就是这个道理
数据变化 就是“发布者”
收集数据通知试图就是“订阅者”
now 我们需要创建一个依赖 收集容器
消息订阅器 Dep 用来容纳所有的“订阅者”(被改变的数据)
这个订阅器Dep主要负责收集订阅者 ,数据一旦变化,就会执行对应订阅者的更新函数
创建订阅器Dep
class Dep{
constructor(){
this.subs=[]
},
//添加订阅者
addSub(sub){
this.subs.push(sub);
},
//判读是否添加订阅者
depend(){
if(Dep.target){
this.addSub(Dep.target)
}
},
//通知订阅者 更新
notify(){
this.subs.forEach((sub=>{
sub.update()
}))
}
}
五、把订阅器嵌入defineReactive 函数 中
function defineReactive(obj,key,val){
const dep=new Dep(); //实例化订阅器
Object.defineProperty (obj,key,{
get (){
dep.depend();// 添加订阅者
console.log(‘数据被读取时自动触发’)
return val
},
set(newVal){
val=newVal;
console.log(‘数据改变时触发’)
dep.notify()
}
})
}
分析:四,五
上面定义了一个订阅器Dep类,该类里面 定义了一些属性和方法,这里注意 它有一个静态属性target,这是一个全局唯一的Watcher,为了 同一时间内只能有一个全局的Watcher被计算,另外他的自身属性subs也是Watcher的数组
上述将订阅器Dep添加订阅者的操作设计在getter里面,这是为了让Wacher 初始化时进行触发,因此需要判断是否添加订阅者。在setter函数里面,如果数据变化,就会去通知所有订阅者,订阅者们就会去执行对应的更新的函数,
现在订阅器Dep设计完毕 接下来 设置订阅者 Watcher
六、创建订阅者 (未完待续)