前面简单介绍了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的具体代码。