vue Virtual DOM创建过程
- 在 lifecycle.js 中的mountComponent 中定义了updateComponent,接收_render()方法作为参数,render接收用户传入的render函数
在render.js中定义了_render方法,其中通过call接收render函数,改变内部this,使用了 c r e a t e E l e m e n t 函 数 , c 与 createElement函数, _c与 createElement函数,c与createElement都是执行createElement函数 - 在createElement函数内部最终会返回VNode,但是不是在此处定义,函数主要是处理参数,最后调用_createElement
- 在_createElement内部
先判断 data是否为空,且用_ob_判断是不是响应式数据,是直接用createEmptyVnode返回一个空节点
判断data是不是有is属性,有记录到tag中 ,is是用来判断是否是组件
判断 tag 属性
判断作用域插槽
核心:判断normalizationType,将数组拍平,转为一维数组
核心:下方判断是否是字符串,创建vnode对象,或是组件的处理组件的vnode,或是处理自定义标签
VNode的处理过程
-
在lifecycle.js 中的mountComponent 中定义了updateComponent,找到_update方法的定义
-
利用preVnode判断是不是首次渲染,调用_patch_方法,比较两个vnode差异,把差异更新到真实DOM,返回给el对象
-
patch是在Vue入口内部的注入的,是在 src/platforms/web/runtime/index.js中,会先判断inBrowser是不是浏览器环境
patch是一个高阶函数,柯里化的函数,利用createPatchFunction生成,接收nodeOps:一些dom操作,baseModules:处理指令和ref的 -
patch的实现
patch接收oldVnode,vnode
先判断新的Vnode是否存在,
定义了insertVnodeQueue队列,先插入节点队列,目的是把对应的dom节点挂载到DOM树上,触发对应的insert钩子函数
判断老的oldVnode是否存在,创建节点,但是不进行挂载
如果新老节点都存在,利用sameVnode比较选择器和key值,然后进行 更新操作,进行diff算法的patchVnode
invokeInsertHook函数就是处理节点对应的钩子函数
返回vnode.elm,新的dom元素返回,记录到elm中 -
createElm的实现,把vnode转换为真实dom,挂载到dom树上
先判断vnode是否有elm属性,是判断是不是渲染过
判断是否组件情况
存储data,children,tag:标签名称 等属性
判断是标签节点情况,是否是开发环境,是不是自定义标签,报出警告,之后处理dom元素,处理子元素转换为dom对象,插入到对应的队列中
判断是注释节点时,创建注释节点,插入到节点队列中
判断是文本节点时,调用createTextNode创建文本节点,插入到节点队列中 -
patchVnode对比新旧节点差异,更新到真实dom,就是diff算法的过程
核心就是触发prepatch函数,执行用户传入的钩子函数
触发所有模块的update函数,执行用户传入的update函数
判断有没有文本节点,对比updateChildren函数,判断对应的新老节点的文本内容 -
updateChildren对比新旧节点,更新dom树,如果节点未变化,重用该节点
就是snabbdom的updateChildren方法
先定义新老节点开始,结束的索引,
先判断是否有重复的key,报出警告
开始while遍历,当老开始节点小于老结束节点,新开始节点小于新结束节点的索引,开始判断
先判断老开始节点是否有值,没有的话获取下一个老节点作为索引
判断老结束节点是否有值,获取前一个节点作为结束节点
开始对比数组中的四个顶点,新开始,新结束,老开始,老结束,是利用patchVnode比较节点和子节点,进行一一对比
如果之前索引都不满足,会找到新老想同的key作为对比最后做收尾处理,如果老节点未处理完,插入对应的节点到老节点之后
如果新节点为处理完,移除对应的老节点