callHook调用钩子函数 beforeCreate
initInject和initProvide 其中provide是一个对象用来注入(向下传递)子孙,inject是一个对象或字符串数组 它的key代表了本地绑定的名称,value为其key用于在可用的注入provide中搜索是否有key与inject中from属性同名的,有传递给result 没有则取inject的默认值,最终返回result
initState 初始化inject data props methods computed watch provide 参数 initData将data解析为_data挂载到vm上,这里触发pushtTarget()
created
将template编译到render函数中
render
parse函数生成AST抽象语法树,调用createCompiler函数通过parse optimize generate 生成一个包含ast render staticRenderFns的对象。render函数,通过parseHTML来匹配传入的html,parseStartTag 得到一个起始标签的数据结构,handleStartTag解析出tagName,attrs,同时将match传入start,进行AST语法树的构建,执行预处理,定义root,处理各类v-标签逻辑。
optimize静态标记,generate生成render函数 拼接字符串new Functon得到真正的渲染函数
$createElement生成VNode节点:如果是被监听的data不能生成vnode,会在渲染的时候被监听后死循环?如果不是保留标签则取查找components上是否有定义,不是字符串的时候认为其实组件的构造类直接创建createComponent。
mounted
将渲染函数中的虚拟dom转化为真实的dom
vm.patch(preVnode, vnode)将VNode转化为真实的node节点,兼容不同的平台。
从oldVnode,vnode对比开始 sameVnode 则调用patchVnode,否则直接处理dom,根据虚拟dom,找到对应的父节点createElm一个真实的dom节点插入到父节点中的相应位置,这里createElm处理了组件的初始化钩子,重新激活组件(子组件内部再次进行生命周期的钩子)
patchVnode 新老虚拟dom比对,难点在于updateChildren,索引的移动和节点对比。
nextTick
mounted是组件实例挂载的首次执行的生命周期,在父组件的mounted中开始子组件的生命周期,父组件的mounted不保证所有子组件的挂载完毕,如果想要在保证所有dom渲染完成可以使用nextTick的回调函数(nextTick的第一个函数就是渲染树的求值和渲染)
我们知道任务队列并非只有一个队列,在 node 中更为复杂,但总的来说我们可以将其分为 microtask 和 (macro)task,并且这两个队列的行为还要依据不同浏览器的具体实现去讨论,这里我们只讨论被广泛认同和接受的队列执行行为。当调用栈空闲后每次事件循环只会从 (macro)task 中读取一个任务并执行,而在同一次事件循环内会将 microtask 队列中所有的任务全部执行完毕,且要先于 (macro)task。另外 (macro)task 中两个不同的任务之间可能穿插着UI的重渲染,那么我们只需要在 microtask 中把所有在UI重渲染之前需要更新的数据全部更新,这样只需要一次重渲染就能得到最新的DOM了。恰好 Vue 是一个数据驱动的框架,如果能在UI重渲染之前更新所有数据状态,这对性能的提升是一个很大的帮助,所有要优先选用 microtask 去更新数据状态而不是 (macro)task,这就是为什么不使用 setTimeout 的原因,因为 setTimeout 会将回调放到 (macro)task 队列中而不是 microtask 队列,所以理论上最优的选择是使用 Promise,当浏览器不支持 Promise 时再降级为 setTimeout。
网友评论