前面简单介绍了compile
的最后一步:生成render
函数,还剩下很多分支细节没说,主要是各种内置指令以及自定义组件和slot
,这些结合之后的patch
过程说效果会好一些。
这篇文章主要是介绍patch
函数的入口,把patch
过程当做黑盒子,从整体的角度讲patch
的作用。
patch 入口
在生成render
函数后,下一步就是调用位于src/core/instance/lifecycle.js
的mountComponent
函数,其中一个关键步骤是:
1 | updateComponent = () => { |
在mount
过程中通过将updateComponent
传给Watcher
实例,可以在watcher
对应的Deps
发生改变时重新执行updateComponent
。
updateComponent
又分为两个子步骤:_render
和_update
。前者用于执行render
函数生成虚拟dom
,后者用于比较新旧vnode
。具体看一下_update
的代码:
1 | Vue.prototype._update = function(vnode: VNode, hydrating?: boolean) { |
在Vue
初始化第一次调用_update
时,prevVnode
是undefined
,这样__patch__
实际上做的事情就是将vnode
转成真实的 dom 绘制到页面上;之后再次调用_update
时新旧vnode
都会存在,此时会执行diff
算法,以最小粒度更新 dom。
在Vue.prototype.$destroy
中也会调用__patch__
:
1 | // invoke destroy hooks on current rendered tree |
传入的第二个参数为null
,此时会删除所有vnode
。
那么__patch__
在何处定义的呢?答案是在src/platforms/web/runtime/index.js
中:
1 | // install platform patch function |
只在浏览器环境下才有patch
操作,patch
函数的定义:
1 | // the directive module should be applied last, after all |
涉及到几个配置项,挨个说下:
nodeOps
:封装了许许多多对原生 dom 操作的方法,都比较简单,例如1
2
3export function createComment(text: string): Comment {
return document.createComment(text);
}platformModules
: 平台相关的一些属性的处理,包括attrs
、class
、domProps
、on
、style
和show
。代码位于src/platforms/web/runtime/modules/index.js
,每个子module
都会包含create
和update
两个钩子。baseModules
:是web
和weex
都有的处理,包括directives
和ref
属性的处理。代码位于src/core/vdom/modules/index.js
,每个子module
同样会包含create
和update
两个钩子。
最后我们的createPatchFunction
就是真正的patch
函数定义之处了,代码非常的长,这里先只展示最重要的轮廓:
1 | const hooks = ['create', 'activate', 'update', 'remove', 'destroy']; |
到这里我们就从整体上了解了patch
的入口在哪以及它的作用,会在接下来的文章里详细描述patch
的具体代码。