虚拟DOM(Virtual DOM)是对DOM的JS抽象表示,它们是JS对象,能够描述DOM结构和关系。应⽤ 
的各种状态变化会作⽤于虚拟DOM,最终映射到DOM上。
优点:
- 虚拟DOM轻量、快速:当它们发⽣变化时通过新旧虚拟DOM⽐对可以得到最⼩DOM操作量,配合异步更新策略减少刷新频率,从⽽提升性能
 - 跨平台:将虚拟dom更新转换为不同运⾏时特殊操作实现跨平台
 - 兼容性:还可以加⼊兼容性代码增强操作的兼容性
 
<div id="app"></div><!--安装并引⼊snabbdom--><script src="../../node_modules/snabbdom/dist/snabbdom.js"></script><script>// 之前编写的响应式函数function defineReactive(obj, key, val) {Object.defineProperty(obj, key, {get() {return val},set(newVal) {val = newVal// 通知更新update()}})}// 导⼊patch的⼯⼚init,h是产⽣vnode的⼯⼚const { init, h } = snabbdom// 获取patch函数const patch = init([])// 上次vnode,由patch()返回let vnode;// 更新函数,将数据操作转换为dom操作,返回新vnodefunction update() {if (!vnode) {// 初始化,没有上次vnode,传⼊宿主元素和vnodevnode = patch(app, render())}else {// 更新,传⼊新旧vnode对⽐并做更新vnode = patch(vnode, render())}}// 渲染函数,返回vnode描述dom结构function render() {return h('div', obj.foo)}// 数据const obj = {}// 定义响应式defineReactive(obj, 'foo', '')// 赋⼀个⽇期作为初始值obj.foo = new Date().toLocaleTimeString()// 定时改变数据,更新函数会重新执⾏setInterval(() => {obj.foo = new Date().toLocaleTimeString()}, 1000);</script></body>
整体流程
core/instance/lifecycle.js
// 定义更新函数const updateComponent = () => {// 实际调⽤是在lifeCycleMixin中定义的_update和renderMixin中定义的_rendervm._update(vm._render(), hydrating)}
core/instance/render.js  
_render()  ⽣成虚拟dom
 core\instance\lifecycle.js
_update()负责更新dom,转换vnode为dom 
Vue.prototype._update = function (vnode: VNode, hydrating?: boolean) {const vm: Component = thisconst prevEl = vm.$elconst prevVnode = vm._vnodeconst restoreActiveInstance = setActiveInstance(vm)vm._vnode = vnode// Vue.prototype.__patch__ is injected in entry points// based on the rendering backend used.if (!prevVnode) {// initial rendervm.$el = vm.__patch__(vm.$el, vnode, hydrating, false /* removeOnly */)} else {// updatesvm.$el = vm.__patch__(prevVnode, vnode)}restoreActiveInstance()// update __vue__ referenceif (prevEl) {prevEl.__vue__ = null}if (vm.$el) {vm.$el.__vue__ = vm}// if parent is an HOC, update its $el as wellif (vm.$vnode && vm.$parent && vm.$vnode === vm.$parent._vnode) {vm.$parent.$el = vm.$el}// updated hook is called by the scheduler to ensure that children are// updated in a parent's updated hook.}
platforms/web/runtime/index.js
patch是在平台特有代码中指定的
Vue.prototype.__patch__ = inBrowser ? patch : noop
patch是createPatchFunction的返回值,传递nodeOps和modules是web平台特别实现
// 传入web平台特有节点操作和属性操作export const patch: Function = createPatchFunction({ nodeOps, modules })
- nodeOps 定义各种原⽣dom基础操作⽅法
 - modules 定义了属性更新实现
 
 core\vdom\patch.js
⾸先进⾏树级别⽐较,可能有三种情况:增删改
- new VNode不存在就删;
 - old VNode不存在就增;
 - 都存在就执⾏diff执⾏更新
 

patchVnode
⽐较两个VNode,包括三种类型操作:属性更新、⽂本更新、⼦节点更新
具体规则如下: 
1. 新⽼节点均有children⼦节点,则对⼦节点进⾏diff操作,调⽤updateChildren 
2. 如果新节点有⼦节点⽽⽼节点没有⼦节点,先清空⽼节点的⽂本内容,然后为其新增⼦节点。
3. 当新节点没有⼦节点⽽⽼节点有⼦节点的时候,则移除该节点的所有⼦节点。 
4. 当新⽼节点都⽆⼦节点的时候,只是⽂本的替换。
updateChildren
updateChildren主要作⽤是⽤⼀种较⾼效的⽅式⽐对新旧两个VNode的children得出最⼩操作补丁。执 
⾏⼀个双循环是传统⽅式,vue中针对web场景特点做了特别的算法优化
在新⽼两组VNode节点的左右头尾两侧都有⼀个变量标记,在遍历过程中这⼏个变量都会向中间靠拢。 
当oldStartIdx > oldEndIdx或者newStartIdx > newEndIdx时结束循环。
