首先理解两个点
1. 响应式原理
2. 依赖收集
响应式原理
vue 采 用 的 是 Object.defineProperty()对属性进行监听,故IE8以下浏览器不支持。
// 搞个vue 对象出来
new Vue({
template:
`<div>
<span>{{data1}}</span>
</div>`,
data:{
data1:'data1',
}
})
修改data1的值
// 修改data1 操作
this.data1 = '我被修改了'
此时,vue中的data1属性并不能监听到被修改了.
但是,我们可以通过Object.defineProperty()监听data1的变化
// obj:监听的对象 key:监听的属性 enumerable:是否可枚举 configurable:是否可配置
let obj = new Vue().data,key = 'data1',val = obj[data1];
Object.defineProperty(obj,key,val,{
enumerable:true,
configurable:true,
// 获得监听属性
get(){
return val;
},
// 修改设置属性值
set(newVal){
if(val === newVal) return
}
})
这样,每次修改data1的值,vue这个对象中的data1就能做出相应改变了
依赖收集
首先知道,为甚么有依赖收集这么个东西, 看下面的代码,承接上面
// 搞个vue 对象出来
new Vue({
template:
`<div>
<span>{{data1}}</span>
</div>`,
data:{
data1:'data1',
data2:'data2'
}
})
// 把刚刚的 Object.defineProperty封装到一个方法里面去
function defineReactive(obj,key,val){
Object.defineProperty(obj,key,{
enumerable:true,
configurable:true,
// 获得监听属性
get(){
return val;
},
// 修改设置属性值
set(newVal){
if(val === newVal) return
}
})
}
// vue中的data有data1,data2多个属性,所以我们需要遍历监听多个属性
let obj = new Vue().data
Object.keys(obj).forEach(key=>{
this.defineReative(obj,key,obj[key])
})
问题来了,这时,我们模板中只用到了data1,我们实现监听属性变化时,data2也被监听了,此时我们修改data2的值
this.data2 = '修改data2的值'
由于添加了监听,修改data2,模板即使没使用到data2,但也是会强制渲染一次,这样就浪费了资源
所以我们需要在模板渲染的时候,收集需要监听的属性,也叫做依赖收集
首先,我们需要一个订阅者Dep类,到时候被监听的这些属性叫做观察者 watcher
// 订阅者 存放观察者
class Dep{
constructor(){
this.subs = [] // watchers 全部放到这里面
}
// 添加 watcher 对象
addSub(sub){
this.subs.push(sub)
}
// 通知所有的watcher更新视图
notify(){
this.subs.forEach(watcher=>{
// 订阅它的观察者全部都要更新
watcher.update();
})
}
}
然后,我们再需要一个观察者类
// 观察者
class Watcher{
constructor(){
/* 在 new 一个watcher的时候,将this赋值给Dep.target */
Dep.target = this // Dep.target 其实就是 Watcher 的实例 然后赋到全局中去
}
update(){
console.log("视图更新啦~")
}
}
Dep.target = null
接着,我们开始监听属性了
// 进行依赖收集
function defineReative(obj,key,val){
// 闭包存储一个订阅者 Dep
let dep = new Dep()
Object.defineProperty(obj,key,{
enumerable:true,
configurable:true,
get(){
if(Dep.target){
// 获取当前值,先添加依赖到订阅者中去,也就是收集依赖
dep.addSub(Dep.target)//将当前的target添加到订阅者中去 获取这个值的时候添加依赖 当视图渲染的时候,会去查找相关依赖
}
return val; //返回该值
},
set(newVal){
if(val === newVal) return
dep.notify();//通知订阅者改变
}
})
}
我们又多个属性,需要进行遍历
function observer(obj){
// 对每一个对象进行响应式
Object.keys(obj).forEach(key=>{
this.defineReative(obj,key,obj[key])
})
}
最后一步,new 一个 Vue 实例
class Vue{
constructor(options){
this._data = options.data
observer(this._data) //把数据搞成响应式
new Watcher();//此处可能有疑问,为啥添加依赖在监听后,因为在读取模板的数据时,才会触发getter属性,此时才收集依赖。对模板中存在的数据才添加依赖
// 模拟渲染的过程
console.log('模拟渲染的过程',this._data.text)
}
}
执行 observer() 方法的时候,模板渲染回去执行Dep中的getter()方法收集依赖,修改data中的属性时候,会处罚setter()方法,相同值返回,不同值通知那些依赖于这个属性的那些function还有模板做出相应变化。