虚拟DOM
-
虚拟DOM(vnode)简单点来说就是用JS对象来模拟DOM结构
-
表达方式:将每一个标签都转为一个对象,这个对象有三个属性:tag props children
-
tag: 标签 也可以是组件
-
props: 标签上的属性和方法
-
children: 标签上的内容或者子节点
-
使用虚拟DOM的优点:
-
原生DOM是有非常多的属性和事件的,即使是一个空的div也会占用比较大的内存
-
使用虚拟dom的话,在dom发生变化的时候,通过diff算法和数据改变前的dom对比,计算出需要改变的dom,然后只对变化的dom进行操作就好,而不是更新整个视图
diff算法
-
这个原理的过程如下:
-
1) diff同层比较: 首先需要说明的是,新旧虚拟DOM对比的时候,diff算法只会在同层比较,不会跨级比较。这是一个深度优先的算法,同时是在同层比较的
-
2)patch算法:当响应式数据发生变化的时候,就会触发setter,然后通过依赖上的dep去通知watcher,这时候watcher就会调用patch算法,给真实DOM打补丁
-
patch算法会比较新的vnode和旧的vnode是否是同一类型的节点,即isSameVnode,它的核心思路如下:
-
即比较key值,标签名,标签上的data等是否一致
-
如果不一致,就直接将旧的vnode替换为新的vnode
-
如果一致就进行更深层次的比较
-
3)patchVnode算法:当两个节点的类型是一致的话,再进行patchVnode算法的比较
-
首先判断新的vnode和旧的vnode是否指向同一个对象,如果是,就直接return
-
如果它们都是文本节点,且不相等,就将真实DOM的文本节点设置为新的vnode的文本节点
-
如果旧的vnode有子节点,而新的vnode没有子节点,就删除真实DOM的子节点
-
如果旧的vnode没有直接点,而新的vnode有子节点,就将新的vnode真实化后挂载到真实DOM上
-
如果两者都有子节点,则执行updateChildren算法来比较子节点
-
4)updateChildren算法:
-
该算法用到首尾指针,新的子节点集合和旧的子节点集合,各自有首尾两个指针
-
新的vnode的首尾,和旧的vnode的首尾两两之间进行比较isSameVnode,看是否是同一类型的节点,即共有四种比较,如果有相等的,就把真实DOM上的对象节点移动到新的vnode的所对应的位置上
-
如果这四种比较都是不相等的,那么再用新的vnode中的key值去寻找在旧的vnode中可以复用的节点
为何不用index作为key值
- 比如说,在列表首部新增了一个元素,
- 这时候新的vnode的头部的元素的key值为0,同时旧的vnode的头部的元素的key值也是0,key值是相等的,但是这两个元素实际上是不相等的
- 但是在updateChildren算法中,通过isSameVnode算法,是会将这两个元素判定为相同类型的元素的。。这样就会引发很多不必要的错误。。