什么是虚拟DOM?
虚拟DOM(Virtual DOM)是对DOM的JS抽象表示,它们是JS对象,能够描述DOM结构和关系。应用 的各种状态变化会作用于虚拟DOM,最终映射到DOM上。
优点
- 虚拟DOM轻量、快速:当它们发生变化时通过新旧虚拟DOM比对可以得到最小DOM操作量,配 合异步更新策略减少刷新频率,从而提升性能;
- 跨平台:将虚拟dom更新转换为不同运行时特殊操作实现跨平台;
- 兼容性:还可以加入兼容性代码增强操作的兼容性
是否有必要?
vue 1.0中有细粒度的数据变化侦测,它是不需要虚拟DOM的,
但是细粒度造成了大量开销,这对于大型项目来说是不可接受的。
因此,vue 2.0选择了中等粒度的解决方案,每一个组件一个watcher实例,
这样状态变化时只能通知到组件,再通过引入虚拟DOM去进行比对和渲染。
源码中的流程
mountComponent() core/instance/lifecycle.js
渲染、更新组件
_render core/instance/render.js
生成虚拟dom
_update core\instance\lifecycle.js
update负责更新dom,转换vnode为dom
__patch__() platforms/web/runtime/index.js
__patch__是在平台特有代码中指定的
patch获取
patch是createPatchFunction的返回值,传递nodeOps和modules是web平台特别实现;
export const patch: Function = createPatchFunction({ nodeOps, modules })
platforms\web\runtime\node-ops.js
定义各种原生dom基础操作方法
platforms\web\runtime\modules\index.js
modules 定义了属性更新实现
patch实现
patch core\vdom\patch.js
首先进行树级别比较,可能有三种情况:增删改。
-
new VNode不存在就删;
-
old VNode不存在就增;
-
都存在就执行diffff执行更新;
patchVnode
比较两个VNode,包括三种类型操作:属性更新、文本更新、子节点更新
具体规则如下:
1. 新老节点均有children子节点,则对子节点进行diffff操作,调用updateChildren
2. 如果新节点有子节点而老节点没有子节点,先清空老节点的文本内容,然后为其新增子节点。
3. 当新节点没有子节点而老节点有子节点的时候,则移除该节点的所有子节点。
4. 当新老节点都无子节点的时候,只是文本的替换。
updateChildren
updateChildren主要作用是用一种较高效的方式比对新旧两个VNode的children得出最小操作补丁。
执行一个双循环是传统方式,vue中针对web场景特点做了特别的算法优化。
在新老两组VNode节点的左右头尾两侧都有一个变量标记,
在遍历过程中这几个变量都会向中间靠拢。
当oldStartIdx > oldEndIdx或者newStartIdx > newEndIdx时结束循环。