前言
上一篇文章中分析了html->vnode主要的处理过程,实际上主要是parse + generate解析template构建render函数的过程,实际上vnode的创建是在render执行过程中触发的。
本文主要分析render调用过程以及该过程vnode的创建。
具体分析
在render构建和调用那篇文章中,就提及render的调用实际上是Watcher实例对象创建触发的,主要的处理逻辑如下:
从上图的主要逻辑点可以看出,$mount的处理过程中主要会构建Watcher实例,而Watcher实例最终会执行Vue实例方法_update和_render,这里是核心了。
_render实例方法
_render实例方法实际上主要的处理点就是执行render函数返回其执行结果,核心代码逻辑如下:
var vnode = render.call(vm._renderProxy, vm.$createElement);
return vnode;
在上一篇文章中,知悉了render函数的基本构造,还使用如下实例:
<div>
{{ text }}
</div>
得到的render函数如下:
const render = new Function("with(this) {return _c('div', {attrs: {'id': 'app'}}, [_v('\n' + _s(text) + '\n)])}")
在执行_render函数实际上就是执行render函数,此时会调用:
vm._c:即$createElement
vm._s:即toString函数
vm.text:即触发data响应式,会调用getter函数,获取text最新的值
_s实例方法
_s实例方法的源码如下:
function toString(val) {
return val === null
? ' '
: typeof val === 'object'
// 格式化对象,并指定缩进为2个空格
? JSON.stringify(val, null, 2)
: String(val);
}
主要就是处理null、对象以及数组形式的数据等将其转换为字符串。
_v实例方法
function createTextVNode(val) {
return new VNode(undefined, undefined, undefined, String(val));
}
_v实际上就是createTextVNode函数,用于创建文本类型的虚拟DOM
_c实例方法
该方法是vnode创建的实际出发点,Vue核心方法之一,具体源码如下:
vm._c = function(a, b, c, d) {
return createElement(vm, a, b, c, d, false);
};
createElement整个的处理逻辑如下:
上图是根据最基本的实例梳理的主要脉络,实际上还有很多复杂的处理,但是最为核心的就是调用VNode构造函数创建虚拟DOM:
new VNode(tag, data, children, undefined, undefined, context)
这里需要说明下createElement中a、b、c、d参数表示的含义:
- a:tag,表示标签名
- b:data,表示属性、事件、class、props等的配置对象
- c: children,表示子节点
- d:normalizationType,表示类型,即要如何处理children中的数据
VNode构造函数
VNode构造函数实际上就是定义相关属性,VNode中重要的属性有:
- tag:当前标签名
- data:标签属性、props、事件等对象集合
- children:子节点的VNode数组
- text:当前标签文本内容
- context:上下文对象,即Vue实例对象
var VNode = function(tag, data, children, text, elm, context, componentOptions, asyncFactory) {
this.tag = tag;
this.data = data;
this.children = children;
...
}
简单实例_render函数的处理就是上面整个逻辑:
_render() -> render -> createElement(或createTextVNode)-> new VNode
_render执行render函数生成DOM结构的vnode,接下来就是_update实例方法的调用了。
_update实例方法
_update中处理实际上有两个主要点:
- vm._vnode相关处理,两点:prevNode = vm._vnode,vm._vnode = vnode
- vm.__patch__的调用
prevNode记录更新前的vnode,如果是初始化,那么prevNode就是空,调用__patch__实现vnode -> html的过程,也是diff算法的实现过程,是整个Vue中核心点之一。
_update核心源码如下:
var prevNode = vm._vnode;
vm._vnode = vnode;
if (!prevNode) {
// 初始化
vm.$el = vm.__patch__(vm.$el,vnode, hydrating, false);
} else {
// 更新
vm.$el = vm.__patch__(prevNode, vnode);
}
总结
从上面的分析中可知,vnode的创建实际上是render函数执行过程中_v、_c函数触发的,而响应式data数据也是通过调用触发的,这些都取决于render函数的结构:
with(this) {
// code
}
而this === Vue实例,触发响应式data数据的调用以及相关vnode虚拟DOM的构建。
而_update则负责比较dom节点并替换。