vue是如何实现响应式的? 随之而来便有两个问题:
- vue是如何实现监听实例中的属性的变化的
- 当数据发生变化时,vue是如何做到通知相应的组件,并使界面实现刷新
vue是如何实现监听实例中的属性的变化的
vue通过设置Object.defineProperty() 来监听对象属性的改变。
我们不妨回到一个最简单的例子
<div id="app">
{{message}}
</div>
<script>
const app = new Vue({
el:'app',
data:{
message:'我是data'
}
})
</script>
此时页面是这样监听message的:
- 先在内部拿到一个对象obj(内容即data中的内容)
- 然后通过 Object.defineProperty对原本data中的属性进行重新定义具体操作如下
Object.keys(obj).forEach(key =>{
// 将obj中的每一个属性(key)中的值赋值到value中
let value = obj[key]
// 使用defineProperty将每个value在obj中进行定义,一旦数值发生改变,便会调用set函数
Object.defineProperty(obj,key,{
set(newValue){
value = newValue
},
get(){
return value
}
})
})
当数据发生变化时,vue是如何做到通知相应的组件,并使界面实现刷新
vue通过发布订阅者模式,实现通知。所谓发布订阅者模式,就是谁在使用特定属性(通过解析html文件得知),该属性更新时就会通知该组件(使用get函数获取最新的数值),该模式的大致实现思路如下:
// 发布者
class dep{
constructor(){
this.subs=[]
}
// 添加订阅者
addSub(watcher){
this.subs.push(watcher)
}
// 通知订阅者进行更新
notify(){
this.subs.forEach(item =>{
item.update()
})
}
}
// 订阅者
class watcher{
constructor(name){
this.name = name;
}
//更新函数
update(){
}
}
const dep = new dep();
const w1 = new watcher('张三')
dep.addSub(w1)
了解完响应式原理实现的两大重要技术后,我们重温一下响应式的原理图
原理图
我们在生成一个实例之后,里面的数据就换传入observer中(通过defineProperty实现),里面的每一个key都将会对应生成一个dep对象。在解析的时候,每解析到一个key时,都会生成一个watcher并将其添加到对应dep对象中的subs数组中(发布订阅者模式),而当某个key的值发生改变的时候,便会调用该key对应的dep对象中的notify函数,并通知该dep的所有订阅者调用update函数,最后更新视图,实现响应式。