思想
核心思想:相对于 DOM 对象,原生的 JavaScript 对象处理起来更快。Virtual DOM 本质上就是在 JS 和 DOM 之间做了一个类似于缓存的东西。这就可以类比 CPU 和硬盘,既然硬盘这么慢,我们就在它们之间加个缓存:既然 DOM 这么慢,我们就在它们 JS 和 DOM 之间加个缓存。CPU(JS)只操作内存(虚拟 DOM),最后的时候再把变更写入硬盘(DOM)。并且,每次计算出最少的需要更新的变化,然后再去修改真实的DOM
互联网1.0那种静态页面信息向用户单向传播信息的时代早已远去,网页越来越动态化,因而不可避免的带来一个问题:网页显示的数据需要频频修改,且网页数据会越来越复杂。如果说每一次数据的更新,都要手动进行繁杂的 DOM 操作,那代码会变得非常难以维护,并且开发效率也会变得低下。
直观的解决办法就是:状态发生变化后,用模板引擎重新渲染整个视图,然后用新视图更换掉旧的视图,这样就不用手动操作 DOM 了。(虽然存在很多性能等等问题)当然需要对这种方法做一些优化,避免整棵 DOM 树发生变更。
做法
DOM 树上的结构、属性信息我们都可以很容易地用 JavaScript 对象表示出来,只需要记录它的节点类型、属性,还有子节点。反过来,也可以根据这个用 JavaScript 对象表示的树结构来构建一棵真正的DOM树。
-
比如
-
这是HTML片段
如果用js来表示它,可以表示成:
-
用新渲染的对象树去和旧的树进行对比,记录这两棵树差异。记录下来的不同就是我们需要对页面真正的 DOM 操作,然后把它们应用在真正的 DOM 树上,页面就变更了。这样就可以做到:视图的结构确实是整个全新渲染了,但是最后操DOM的时候确实只变更有不同的地方。
每次计算出最少的需要更新的变化,然后再去修改真实的DOM
数据改变 -> 虚拟DOM(计算变更) —> 操作真实的DOM -> 视图更新
具体步骤:
- 用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中
- 当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异
- 把2所记录的差异应用到步骤1所构建的真正的DOM树上,视图就更新了
原始的比较算法存在效率问题,需要对比较算法做优化:
- 只比较同一层级,不跨级比较
- 标签名不同,直接删除,不继续深度比较
- 标签名相同,key不同,就认为是不同节点,不继续深度比较
过程:
-
如果新的vNode是text节点
- 如果老的vnode有子节点,那就把子节点全都删掉
- 再把text设置成新vnode的text
-
如果新vnode不是text节点
- 如果老vnode也有子节点,那就对元素进行更新
- 如果老的vnode是一个text节点,那就把元素的text的清空掉,然后再把新vnode的子节点添加进来
- 如果新vnode没有子节点,那么移除掉元素的子节点
- 如果老vnode有text节点