Vue之vnode创建

Vue之vnode创建前言上一篇文章中分析了 html gt vnode 主要的处理过程 实际上主要是 parse generate 解析 template 构建 render 函数的过程 实际上 vnode 的创建是在 render 执行过程中触发的 本文主要分析 render 调用过程以及该过程 vnode 的创建 具体分析在 render 构建和调用那篇文章中 就提及 render 的调用实际上是 Watcher 实例对象创建触发的 主要的处

前言

上一篇文章中分析了render函数生成的主要细节,实际上主要是parse + generate解析template构建render函数的过程。当render函数构建过后,接下来就生成一个watcher实例,该实例与视图渲染密切相关,会触发render函数执行。

实际上VNode对象的创建是在render调用过程中触发的,这是render函数本身的结构导致的。本文主要分析render调用过程中vnode的创建。

具体分析

从上图的主要逻辑点可以看出,$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函数,此时会调用:

_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); }; 

上图是根据最基本的实例梳理的主要脉络,核心是调用VNode构造函数创建虚拟DOM:

new VNode(tag, data, children, undefined, undefined, context)

实际上HTML标签、SVG标签、自定义组件都是通过createElement来生成对应的Vnode对象的。

这里需要说明下createElement中a、b、c、d参数表示的含义:

  • a:tag,表示标签名
  • b:data,表示属性、事件、class、props等的配置对象
  • c: children,表示子节点
  • d:normalizationType,表示类型,即要如何处理children中的数据

实际上对于自定义组件这里的逻辑就需要具体聊聊了,creaElement关于这边的逻辑如下:

if (isDef(Ctor = resolveAsset(context.$options, 'components', tag))) { 
    // component vnode = createComponent(Ctor, data, context, children, tag); } 

而createComponent函数的逻辑自然是处理与组件相关的属性例如props、事件等从而创建Vnode对象,但是其中有一个重要的逻辑是installComponentHooks。

自定义组件对应的虚拟节点VNode对象有一些Hooks(钩子函数):

var componentVNodeHooks = { 
    init: function init() { 
   }, prepatch: function prepatch(oldVnode, vnode) { 
   }, insert: function insert(vnode) { 
   }, destroy: function destroy(vnode) { 
   } }; 

实际上定义初始化、patch之前、插入、销毁几个阶段的钩子,这些钩子是用于patch阶段用来创建、更新、销毁组件的。

实际上这里还隐藏了一个非常重要的逻辑信息:

所有通过createElement创建的组件都是子组件,而所有子组件在render函数执行时只是创建了虚拟节点VNode对象而已。每一个组件都是一个Vue实例,子组件的Vue实例创建是在patchVnode(即diff算法)阶段才生成的

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); } 

总结

render函数的结构:

with(this) { 
    // code } 
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/224820.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月17日 上午11:04
下一篇 2026年3月17日 上午11:04


相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号