Open lihongxun945 opened 6 years ago
既然是在$mount中编译模版、初始化watcher,那为何在created勾子中改变数据的值,也会触发相应的更新?这个时候watcher不是还没有初始化吗
既然是在$mount中编译模版、初始化watcher,那为何在created勾子中改变数据的值,也会触发相应的更新?这个时候watcher不是还没有初始化吗
render watcher没有初始化,其他的watcher如watch和computed选项已经初始化了
回顾$mount 之前发生了什么
借用 Vue 官方这张经典的图来说明:
然我们回顾下在我们调用
$mount
挂载组件之前都发生了什么?,如上图所示,主要发生了这么些事情:initMixin
,stateMixin
等各种xxxxMixin
在Vue.prototype
上添加属性和方法,这在图中是没有显示的,因为这不是组件的生命周期,而是类的创建。new Vue()
开始构建Vue实例,组件的生命周期在这里开始。_init
函数中做mergeOptions
合并配置,其中包括一些由Vue提供的默认配置比如directives
等。图中并没有显示这一块beforeCreate
阶段,还是在_init
函数中, 做events
,lifecycle
等初始化create
阶段,做数据响应和数据注入。template
编译为render
函数,到这里就完成了mount
的准备工作。在
_init
结束之后,会调用$mount
挂载组件,进入一个新的阶段mount
阶段。那么,从$mount
函数开始,到生成真实DOM,中间经历了哪些步骤呢?Mount 开始
首先我们看
$mount
函数的定义:platforms/web/runtime/index.js
可以看到它主要是调用了
mountComponent
函数,这个函数我们在之前讲到过,他会创建一个Watcher
来监听vm
的变动,一旦发生任何变化都会调用vm._update
更新组件。mountComponent
核心的代码如下所示:core/instance/lifecycle.js
由于这里
lazy === false
所以在创建watcher
的时候会立刻进行一次求值,也就是调用vm._update(vm._render(), hydrating)
方法更新组件。这里的第一个参数是vm._update
的结果,因此我们先看看这个函数做了什么:core/instance/render.js
上面的代码经过了我的简化。我们可以看到,
_render
函数主要作用就是调用了_renderProxy
生成vnode
,并返回,而_renderProxy
其实就是调用了我们编译好的render
函数。所以我们知道了,每当组件有任何更新的时候,都会调用render
函数生成新的vnode
。那么我们再看updateComponent
函数,当组件更新的时候就变成了这样:因为我们更新了
vnode
,这个_update
函数的目的显然是会进行patch
,把更新同步到真实的DOM
上。来看看_update
函数的代码:core/instance/lifecycle.js
正如我们所料,
_update
函数主要做的事情就是调用了__patch__
方法。__patch__
方法会负责把vnode
渲染为真实的 DOM.我画了一个图,来表示整个$mount
以及update
的过程:$mount
函数其实是创建了上面这个流程,而不是发起了一次 渲染,因为在创建watcher的时候lazy === false
所以创建完成之后立刻会触发一次更新。图中的两个重要阶段
render
和patch
我们分别在接下来的两章详细讲解。下一章,让我们看看
render
函数是如何生成vnode
的。下一章:Vue2.x源码解析系列九:vnode的生成与更新机制