_update
方法挂载在vue原型上,调用的时机有两个,一个是首次渲染的时候把我们的vnode
映射成虚拟dom
,另一个就是我们改变数据的时候,数据改变驱动视图的变化,同样也会调用_update
初始化渲染的时候调用vm.__patch__
因为vue
也可以运行在服务端,是没有dom
的。如果是浏览器环境就调用patch
方法否则就是空函数,
patch
方法里面调用createPatchFunction
,参数nodeOps
是我们一些操作dom的方法,
参数modules
是platformModules
和baseModules
的concat的合集,这个里面定义了很多类的钩子函数,在patch过程中会调用不同模块的钩子函数
createPatchFunction
就是在里面调用不同模块的钩子函数
中间还定义了很多辅助函数,最后返回了一个patch
函数,所以我们调用vm._patch
的时候实际上是调用的createPatchFunction
中返回的patch
方法,Vue为什么要绕了一大圈把patch方法定义在这里,其实用到了函数柯里化的技巧。createPatchFunction
调用传入的参数是与跨平台有关的,vue可以运行的web
和weex
平台,调用的dom
的api
是不一样的,所以分离作为参数nodeOps
传入,另外一个就是modules
,这些模块不同的生命周期要做的事情,怎么去生成的模块和dom
做配合这些也是和平台相关的,用函数柯里化的技巧把参数一次传入,就不用判断平台的差异,因为之前已经抹平了。相当于利用了闭包的技巧实现了nodeOps和modules的持有。
patch方法中判断oldValue.nodeType是不是一个真实的dom
,因为我们不是服务端渲染,所以最后走到了oldVnode = emptyNodeAt(oldVnode)
,
emptyNodeAt
把我们真实的dom
转成一个vnode
,
oldElm
是id为app的div
,那么parentElm
就是body
,接下来createElm
是一个很重要的函数,它的在作用就是把vnode挂载到真实的dom上
折叠起来的判断走不到,后面判断tag是不是未知的元素,如果是就报警告 Unknown custom element:<xxx> did you ...
vnode
的elemenet
如果有命名空间就调用命名空间的创建createElementNS
方法,否则直接调用createElement
创建,
平台的判断可以不用看,如果你的vnode
的children
还是vnode
接下来创建子节点
createChildren
遍历vnode
,判断children
是不是array
类型,递归调用createEle
,然后把当前节点作为父节点插入,如果就是普通的文本节点,nodeOps
直接调用appendChild
添加进来。
最终调用insert
方法插入节点
insert辅助方法,判断参考节点ref
的父节点和parent
是不是下相等,从而调用insertBefore
和 appendChild
方法
如果这里定义了parentElm
,那就要把之前的节点移除掉,最终调用invokeInsertHook
钩子函数