先上结论
- 在new Vue()实例里面的data里的数据会通过Obsever重新定义一遍
- Observer其实就是()
- Vue实例里面的el模板会通过compile解析之后再页面上初始化
- 因为初始化的时候调用了()里的get方法,所以会通过Watcher添加到了订阅者里
- 此时,当Vue实例里的data数据发生改变的时候,先是调用了()里的set方法
- 然后通过Dependence里的notify去通知每一个订阅者去执行自己的updata方法,然后更改页面显示
直接上代码
<div id='app'>
<h1>{{message}}</h1>
<h1>{{message}}</h1>
<h2>{{name}}</h2>
</div>
const app = new Vue({
el:'#app',
data:{
message:'哈哈哈',
name:'liu'
}
})
数据响应的步骤
data的数据传到Vue实例里面
Vue内部会做出
message:'哈哈哈',
name:'liu'
Object.keys(obj).forEach(key => {
let value = obj[key]
})
开始监听对象值的改变
- 原来的属性不好监听,把原来的属性全部重新通过重新定义一遍
Object.keys(obj).forEach(key => {
let value = obj[key]
Object.defineProperty(obj,key,{
set(newValue){
console.log(`监听${key}的改变`)
},
get(){
console.log(`获取${key}对应的值`)
return value
}
})
})
被监听的数据改变之后应该告诉谁?
<div id='app'>
</div>
- 根据解析html代码,获取到那些再用这个属性
- 在用这个属性的时候肯定会调用一次message的 get 方法
- 从而获取那些地方在用这些属性
- 当属性发生改变的时候,就去通知这些地方
当数据发生改变,Vue是如何知道通知那些数据的
创建 dependency
- constructor里的数组就是用来记录谁订阅了这个属性的
- 通过addSub方法添加订阅者
class Dependency {
constructor() {
this.subscribe = []
}
addSub(watcher) {
this.subscribe.push(watcher)
}
}
创建 Watcher
class Watcher{
constructor(name) {
this.name = name
}
}
一旦有人调用属性(message)的get方法,就赶紧创建一个
const watcher1 = new Watcher('watcher1再用属性')
Object.keys(obj).forEach(key => {
get(){
const watcher1 = new Watcher('watcher1再用属性')
return value
}
})
})
- 还要通过dep的addSub方法把watcher1加到dep里面
dep.addSub(watcher1)
给 dependency 添加 notify 方法
- notify 会遍历所有订阅者(subscribe)们,提醒他们去执行update方法刷新数据
class Dependency {
constructor() {
this.subscribe = []
}
addSub(watcher) {
this.subscribe.push(watcher)
}
notify() {
this.subscribe.forEach(item => {
item.update()
})
}
}
给 wather 添加update方法
class Watcher{
constructor(name) {
this.name = name
}
update(){
console.log(`${this.name}发生更新了`)
}
}
一旦属性(message)发生改变,就回去调用class Dependenc里的notify方法
Object.keys(obj).forEach(key => {
let value = obj[key]
Object.defineProperty(obj,key,{
set(newValue){
console.log(`监听${key}的改变`)
value = newValue
dependency.notify()
},
get(){
console.log(`获取${key}对应的值`)
return value
}
})
})
Vue如何检测数组的变化
- 数组没有使用去重新定义
- 函数劫持:在函数里去通知数据更新
- Vue把的数组的原型方法进行了重写,重写一个新的原型,让数组类型通过这个原型链,找到新写的原型,如果用户调用数组上的方法,那走的就是我们自己的方法,就可以去设置响应式了
数组里的每一项有可能还是一个对象,把数组遍历一下,如果数组里有对象的话,会继续深度遍历,
- 对数组里的每一项进行观测,如果是对象的也会进行数据更新数组里的对象会再次进行Observer
- 重写后的数组原型上拥有的方法是有限的,都是一些能够改变数组的方法
- 如何处理循环依赖:在Vue进行检测的时候如果发现这个数据已经被检测过的了,就不会观测,直接把观测后的数据返回了,不会重复检测
- 源码
data:{
arr:[]
}
data.arr.__proto__ == arrayMethods
- arrayMethods 就是Vue内部重写后的数组原型