vue响应式原理的基本流程:
初始化Vue实例时,Observer遍历data里所有属性,使用Object.defineProperty()方法把这些属性都转为getter/setter。并且创建dep管理器(一个属性一个Dep,用来管理该属性下的所有Watcher,如果同一个属性在DOM节点中多次使用会创建多个Watcher)
在解析指令时,创建Watcher,将更新函数放到Watcher的回调上。
初始化视图时,会读取属性值,触发get,将创建的Watcher添加到dep中。
当修改数据时,触发set,调用dep的notify,通知该dep内部所有Watcher的执行回调,重新render当前组件,生成新的虚拟DOM树。
Vue框架会遍历并对比新虚拟DOM树和旧虚拟DOM树种每个节点的差别,并记录下来,最后,加载操作,将所有记录的不同点,局部修改到真DOM树上。
Object.defineProperty()方法大家都知道,用来监测一个对象的值,通过getter和setter属性进行劫持属性的操作和更新
const data = {
name: '',
}
// 拷贝一下data对象 防止死循环
let newData = { ...data }
Object.defineProperty(data, 'name', {
get: () => {
console.log('读取')
return newData.name
},
set: (newval) => {
console.log('设置')
newData.name = newval
response()
},
})
setTimeout(() => {
data.name = 'hello world'
}, 1000)
//输入时为data重新赋值 通知监听的方法为newData更改数据来达到页面新数据的动态呈现
iptName.oninput = function () {
data.name = this.value
}
const response = () => {
hName.innerText = data.name
iptName.value = data.name
}
//简单实现一个Object.defineProperty()
复制代码
创建Observer 递归监控劫持数据
现在开始考虑我们上面的data有多个属性,我们就需要一个方法或者类去遍历这个对象
//先为Object.defineProperty()封装一个公共的方法 并简化方法里面的操作
function defineReactive(data, key, value = data[key]) {
observe(value)//监听子属性
Object.defineProperty(data, key, {
get() {
return value
},
set(newValue) {
value = newValue
}
})
}
创建Observe函数去递归实现数据劫持
const observer =(data) => {
Object.keys(data).forEach((k) => defineReactive(data, k))
}
const data = {a:1,b:{c:1}}
observer(data)
复制代码
订阅者Dep
接下来我们实现一个消息的订阅器,用来收集订阅者,数据改变的话则触发notify方法,再通过调用订阅者自身的update方法
function defineReactive(data, key, value = data[key]) {
var dep = new Dep()
observe(value)//监听子属性
Object.defineProperty(data, key, {
get() {
return value
},
set(newValue) {
value = newValue
dep.notify() //通知所有的订阅者
}
})
}
function Dep(){
this.subs = []
}
//给Dep的原型上追加add和notify方法
Dep.prototype = {
addSub(sub) {
this.subs.push(sub);
},
notify() {
this.subs.forEach(function(sub) {
sub.update();
});
}
};
//我们在Object.defineProperty的Setter方法中添加了dep.notify(),那么我们就需要在合适的地方通过上面的dep添加订阅者
//我们选择在getter的时候添加订阅者
Object.defineProperty(data, key, {
get() {
Dep.target&&dep.addSub(Dep.target);
return value
},
set(newValue) {
value = newValue
dep.notify() //通知所有的订阅者
}
})
复制代码
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END