响应式数据原理(参考https://juejin.cn/post/6844903597986037768)
Observer(响应式绑定)、Dep(依赖收集)、Watcher(订阅者)
个人理解:
Vue在初始化时候会调用initDate()方法,通过vm.$options.data获取数据,调用Observer()方法进行对数据进行观测也就是响应式绑定:
1. 是否被观测过,有就return
2. 是否是对象:
(1)对象(非数组):调用walk()方法,使用Object.keys()遍历对象属性,——重复步骤
(2)数组:将数组__proto__指向改写的arrayMethods方法,这个方法主要拦截改写数组七个改变自身数值方法(push、pop、shif、unshift、splice、sort、reverse),对新增的元素进行观测,——重复步骤
(3) 非对象:——重复步骤
重复步骤:并调用defineReative这个方法重写了defineProperty的get、set方法,get方法调用了dep.depend()收集当前属性与watcher有关的 依赖。set方法则监听该属性,当不一致时,调用dep.notify(),进而调用当前属性对应所有Watcher的update()方法进行更新。
简单实现:https://juejin.cn/post/68449041464248238220
双向绑定:
let vue = {
$data = { a: 1, b: 2 }
}
for(let key in vue.$data) {
Object.defineProperty(Vue, key, {
configurable: true, // 可配置
enumerable: true, // 可枚举
get() {
return value.$data[key]
},
set(newV) {
value.$data[key] = newV
}
})
}
Diff算法:(https://juejin.cn/post/6994959998283907102)
概念:
虚拟dom: 用来表示真实dom的对象
虚拟DOM算法操作真实DOM,性能高于直接操作真实DOM,虚拟DOM和虚拟DOM算法是两种概念。
dif算法:是一种对比算法,用以对比新旧虚拟节点,找出更改数据并更新对应的真实节点,而不用更新没有改变的部分节点,实现精确更新真实节点,进而提高效率。
新旧虚拟DOM对比的时候,Diff算法比较只会在同层级进行, 不会跨层级比较。 所以Diff算法是:深度优先算法。 时间复杂度:O(n)
个人理解diff算法对比流程:
当数据改变时,触发defineReative方法改写的Object.defineProperty的set()方法,进而调用dep.notify()通知所有依赖该属性的订阅者watcher进行更新,订阅者们就会调用patch()方法,也就是diff算法对比,给真实DOM打补丁,更新相应视图。
patch( 补丁)方法调用isSameVnode判断同层新旧虚拟节点是否相同,不相同直接整个接点替换成新虚拟节点,更新视图;相同则继续执行patchVnode()方法进行深层比对。
patchVnode()主要进行5个判断
先获取真实节点el。
1. 判断newVnode、oldVnode是否是同一个对象,如果是,return。
2. 判断是否有文本节点并且不相同,则el文本节点设置为newVnode文本节点。
3. 判断OldVnode有子节点,NewVnode没有子节点,则删除el子节点。
4. 判断OldVnode没有子节点,NewVnode有子节点,则el添加NewVnode子节点。
5. 判断两者都有子节点,执行updataChildren()方法比较子节点。
updataChildren()主要通过首尾对比算法对子节点进行对比。主要进行5个判断:(真实Node和旧虚拟Node是相同的)
1. oldS和newS调用sameVnode()判断,相同真实Node对应old节点位置移动到new节点对应位置,oldS++, newS++ ,不同则不动。
2. oldS和newE调用sameVnode()判断,相同真实Node对应old节点位置移动到new节点对应位置, oldS++, newE-- ,不同则不动。
3. oldE和newS调用sameVnode()判断,相同真实Node对应old节点位置移动到new节点对应位置,oldE--, newS++ ,不同则不动。
4. oldE和newE调用sameVnode()判断,相同真实Node对应old节点位置移动到new节点对应位置,oldE--, newE-- ,不同则不动。
5. 如果以上逻辑都匹配不到,再把所有旧子节点key做一个key -> index映射表,再用新Vnode的key对应映射表找到旧Vnode可复用位置,然后真实Node对应old节点位置移动到new节点对应位置。
6. 最后oldS>oldE时,则旧子节点遍历完成,对比新旧节点长度,多了插入对应位置,少了删除对应节点。
具体代码看https://juejin.cn/post/6994959998283907102
网友评论