2021-7-26号笔记-Vue数据响应式原理
对象的响应式原理
1、 侦听数据
2、 收集依赖
3、 更新数据
过程:
1、在类Observer中,将vue中的data传入构造函数construstot中,进行保存
2、 判断是数组还是对象,如果是对象,就调用walk方法,遍历对象中的key与value,并且传入defineReactive()方法中。将这些数据变成响应式数据,进行劫持数据。
3、 defineReactive() 这个方法,首先会对上面遍历出来的val进行判断,如果还是对象,那么在new一个Observer,将该对象传入。之后new Dep出来用来收集依赖,之后调用object.defineProperty方法,里面有get和set方法,get用来收集依赖,将调用dep里面的addSub来收集依赖,而set方法,则会调用dep上的notify方法,而notify方法,会对所有的依赖队列里的依赖进行遍历,并且会对遍历出来的依赖进行调用watcher上面的update方法,进行数据更新。
export default class Observer {
// 首先在constructor中对vue中的data进行保存、
constructor (data) {
this.value = data
// 判断是数组还是对象
if (Array.isArray(this.value)) {
// 劫持数组,我们暂且放这。
} else {
// 如果时对象,那么就调用walk方法
this.walk(this.value) // vue内部用来递归劫持数据的方法
}
}
walk (obj) {
const keys = Object.keys(obj)
for (let i = 0; i < keys.length; i++) {
// 遍历对象中的值,调用defineReactive方法,将对象里面的值,变为响应式的值
defineReactive(obj, keys[i], obj[keys[i]])
}
}
}
function defineReactive(data, key, val) {
// 新增判断当前属性值是否是对象,如果是对象,继续递归劫持
if (typeof val === 'object') {
new Obsever(val)
}
let dep = new Dep()
Object.defineProperty(data, key, function() {
enumerable: true, // 该属性是否可被枚举
configurable: true, // 该属性的配置后续是否可进行更改
get: function () { // 用于获取属性值
// 收集依赖
if (window.target) {
// 调用dep上的addSub方法 将target添加到依赖队列上
dep.addSub(window.target)
}
return val
},
set: function (newVal) { // 设置属性值
if (newVal === val) return
// 用新值替换旧值
val = newVal
// 数据发生变化时,通知依赖者
dep.notify()
}
})
}
export default class Dep () {
constructor() {
this.sub = [] // 依赖者数组,用来存放依赖者watcher实例
}
addSub (sub) { // 添加依赖
this.sub.push(sub)
}
remove (sub) { // 删除依赖
remove(this.sub, sub)
}
// 通知方法,用于通知各个依赖者进行数据更新
notify () {
const arr = this.subs.slice() // 先拷贝一份
for (let i = 0; i < arr.length; i++) {
arr[i].update() // 让每一个watcher去调用自己的更新函数
}
}
}
export function remove (subs, sub) {
if (subs.length && subs.length > 0) {
const index = subs.indexOf(sub)
if (index > -1) {
return subs.splice(index, 1)
}
}
}