探索Vue.js底层源码——createElement是如何创建Vnode的?

前言

    在Vue的渲染过程中都会先将代码中的标签转化成render函数,然后通过render函数中的creatElement来创建Vnode。那么,在Vue中createElement又是怎么定义的?

createElement()方法

    在Vue.js开发中可以利用createElement方法创建VNode,createElement()方法定义在Vue源码的 src/core/vnode/create-element.js 中

	export function createElement (
	  context: Component, // vm实例
	  tag: any, // vnode的标签
	  data: any, // vnode的数据
	  children: any, // vnode子节点
	  normalizationType: any, 
	  alwaysNormalize: boolean
	): VNode | Array<VNode> {
	  // 参数的重载
	  // 如果data是数组或data是string、number、symbol、boolean
	  if (Array.isArray(data) || isPrimitive(data)) {
	    // 令nomalizationType = children
	    normalizationType = children
	    // 令children等于data
	    children = data
	    // 令data为undefined
	    data = undefined
	  }
	  // 判断alwaysNormalize是否为布尔值true
	  if (isTrue(alwaysNormalize)) {
	    // 令normalizationType = 1
	    normalizationType = ALWAYS_NORMALIZE
	  }
	  // 调用_context传入context、tag、data、children、normalizationType
	  // 即定义createElement这个方法是参数为6个
	  // 但实际上调用的是_createElement,即createElement是对_createElement进行包装
	  // 并且调用_createElement实际用到只有5个参数
	  // context vm实例、tag vnode的标签、data vnode的数据、normalizationType数值
	  return _createElement(context, tag, data, children, normalizationType)
}

    可以看到在createElement()方法中只是对传入的参数进行了一些操作,然后调用了_createElement()方法,即在vnode创建的过程中,几乎都是_createElement()方法完成的,而_createElement()方法与createElement()方法一样定义在这个文件中(下面只摘出部分_createElement()源码)

	export function _createElement (
	  context: Component, // vm实例
	  tag?: string | Class<Component> | Function | Object, // Dom标签
	  data?: VNodeData, // vnode数据
	  children?: any,
	  normalizationType?: number
	): VNode | Array<VNode> {
	  // isDef判断data是否已定义且不为空
	  // isDef(data.__ob___)判断data是否是具备观察者属性
	  if (isDef(data) && isDef((data: any).__ob__)) {
	    // 如果是开发环境
	    // 抛出错误提示:不能使用已被观察的数据对象作为虚拟节点的数据
	    // 通常都是创建新的vnode的data对象在每一个渲染函数中
	    process.env.NODE_ENV !== 'production' && warn(
	      `Avoid using observed data object as vnode data: ${JSON.stringify(data)}\n` +
	      'Always create fresh vnode data objects in each render!',
	      context
	    )
	    // 创建一个空的虚拟节点返回
	    return createEmptyVNode()
	  }
	  // object syntax in v-bind
	  // 判断是否带有v-bind语法
	  if (isDef(data) && isDef(data.is)) {
	    // 令tag等于data.is
	    tag = data.is
	  }
	  // 如果vnode标签不存在
	  if (!tag) {
	    // 返回一个空格vnode
	    return createEmptyVNode()
	  }
  // 如果此时是开发环境且data是已定义不为null且data.key是原始的类型
  if (process.env.NODE_ENV !== 'production' &&
    isDef(data) && isDef(data.key) && !isPrimitive(data.key)
  ) {
  	....
  }
  // 判断vnode的children是否为数组,且第一个元素是否为function
  if (Array.isArray(children) &&
    typeof children[0] === 'function'
  ) {
    data = data || {}
    data.scopedSlots = { default: children[0] }
    children.length = 0
  }
  // 如果normalizationType为ALWAYS_NORMALIZE
  if (normalizationType === ALWAYS_NORMALIZE) {
    // 改造children数组
    children = normalizeChildren(children)
  } else if (normalizationType === SIMPLE_NORMALIZE) { 
    // 改造children数组
    children = simpleNormalizeChildren(children)
  }
  let vnode, ns
  // 如果tag是string
  if (typeof tag === 'string') {
    let Ctor
    ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag)
    if (config.isReservedTag(tag)) {
      ...
      vnode = new VNode(
        config.parsePlatformTagName(tag), data, children,
        undefined, undefined, context
      )
    } else if ((!data || !data.pre) && isDef(Ctor = resolveAsset(context.$options, 'components', tag))) {
      vnode = createComponent(Ctor, data, context, children, tag)
    } else {
      vnode = new VNode(
        tag, data, children,
        undefined, undefined, context
      )
    }
  } else {
    vnode = createComponent(tag, data, context, children)
  }
  if (Array.isArray(vnode)) {
    return vnode
  } else if (isDef(vnode)) {
    if (isDef(ns)) applyNS(vnode, ns)
    if (isDef(data)) registerDeepBindings(data)
    return vnode
  } else {
    return createEmptyVNode()
  }
}

    _cleamElement()方法接受5个参数:context(vm实例),tag( 标签名|组件|函数)、data(vnode数据)、children 、 nomalizationType。在这个方法中创建Vnode,会先对children进行判断,如果只是简单的一维数组,则遍历创建每一个子vnode;如果children是二维即以上的,则会将children拆开并合并成一维数组,然后遍历创建每一个vnode。在创建好后,在创建父vnode,然后将子vnode作为父vnode的children进行赋值,从而完成vnode的创建过程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值