【Vue源码】第七节数据驱动之认识createElement

我们知道VNode是由_render函数生成的:

vnode = render.call(vm._renderProxy, vm.$createElement);

$createElement中:

vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false);
vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true);

VNode的创建是跟createElement有关系。

// src/core/vdom/create-element.js

// wrapper function for providing a more flexible interface
// without getting yelled at by flow
export function createElement (
  context: Component,
  tag: any,
  data: any,
  children: any,
  normalizationType: any,
  alwaysNormalize: boolean
): VNode | Array<VNode> {
    
    
  // 判断data是否时数组或者原始类型(不包括null、undefined)
  // 在我们一开始写的例子,在页面上解析message的案例,改变成render函数时,data是string,所以不用往前移动
  if (Array.isArray(data) || isPrimitive(data)) {
    
    
    // 参数都往前移动一位
    normalizationType = children
    children = data
    data = undefined
  }
  // vm._c = (a, b, c, d) => createElement(vm, a, b, c, d, false);
  // vm.$createElement = (a, b, c, d) => createElement(vm, a, b, c, d, true);
  if (isTrue(alwaysNormalize)) {
    
    
    // const SIMPLE_NORMALIZE = 1
    // const ALWAYS_NORMALIZE = 2
    normalizationType = ALWAYS_NORMALIZE
  } 
  // 到这一步可以看出来createElement是_createElement的封装
  return _createElement(context, tag, data, children, normalizationType)
}
// src/core/vdom/create-element.js
export function _createElement (
  context: Component,
  tag?: string | Class<Component> | Function | Object,
  data?: VNodeData,
  children?: any,
  normalizationType?: number
): VNode | Array<VNode> {
    
    
  // 判断data是否含有__ob__属性, Vue中被j监听的data都会添加上__obj__
  if (isDef(data) && isDef((data: any).__ob__)) {
    
    
    // 抛出警告的代码(省略)
    // 返回空的VNode
    return createEmptyVNode()
  }
  // 判断是否时动态组件(即有没有is属性)
  if (isDef(data) && isDef(data.is)) {
    
    
    tag = data.is
  }
  // 如果tag是false
  if (!tag) {
    
    
    // 返回空的VNode
    return createEmptyVNode()
  }
  // warn against non-primitive key
  if (process.env.NODE_ENV !== 'production' &&
    isDef(data) && isDef(data.key) && !isPrimitive(data.key)
  ) {
    
    
    if (!__WEEX__ || !('@binding' in data.key)) {
    
    
      warn(
        'Avoid using non-primitive value as key, ' +
        'use string/number value instead.',
        context
      )
    }
  }
  // 跟插槽有关的代码省略
  // ...
  // 规范化处理
  if (normalizationType === ALWAYS_NORMALIZE) {
    
    
    children = normalizeChildren(children)
  } else if (normalizationType === SIMPLE_NORMALIZE) {
    
    
    children = simpleNormalizeChildren(children)
  }
 
  let vnode, ns
  // 如果tag是string类型
  if (typeof tag === 'string') {
    
    
    let Ctor
    ns = (context.$vnode && context.$vnode.ns) || config.getTagNamespace(tag)
    // 判断是不是html的内置标签
    if (config.isReservedTag(tag)) {
    
    
      // platform built-in elements
      vnode = new VNode(
        config.parsePlatformTagName(tag), data, children,
        undefined, undefined, context
      )
    // 判断tag是不是已注册的组件名
    } else if ((!data || !data.pre) && isDef(Ctor = resolveAsset(context.$options, 'components', tag))) {
    
    
      // component
      vnode = createComponent(Ctor, data, context, children, tag)
    } else {
    
    
      // 一个未知的标签名,这里会直接按标签名创建 VNode
      vnode = new VNode(
        tag, data, children,
        undefined, undefined, context
      )
    }
  } else {
    
    
    // direct component options / constructor
    vnode = createComponent(tag, data, context, children)
  }	
  
  // 最后对vnode又做了一次校验,最后返回vnode
  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()
  }
}

// template解析成render后会执行的函数,将把二维数组拍平成一维数组
export function simpleNormalizeChildren (children: any) {
    
    
  for (let i = 0; i < children.length; i++) {
    
    
    if (Array.isArray(children[i])) {
    
    
      return Array.prototype.concat.apply([], children)
    }
  }
  return children
}

// 用户自定义写的render函数
export function normalizeChildren (children: any): ?Array<VNode> {
    
    
  // 如果时自定义类型,则返回一个文本节点,例如例子上的message,直接解析成文本节点
  return isPrimitive(children)
    ? [createTextVNode(children)]
    : Array.isArray(children)
      // 如果不是 再做处理,最后也是返回一个一维数组
      ? normalizeArrayChildren(children)
      : undefined
}

总结

VNode是由_render中的createElement返回的,而createElemnt实际上是对_createElement的封装。在_createElement中会对参数进行处理(转为需要的格式)并进行判断,这些参数后期都会成为VNode所需的参数的值,传递的data不能是被监听过的值,tag可以是字符串(为内置的html、或者已经组册过的组件,如果都不是,也会创建VNode,然后运行时再检查),也可以是组件的名字,最后会再次检查生成的VNode,并返回。

下一节学习如何将生成的VNode渲染在视图上,即成为真实的DOM。

猜你喜欢

转载自blog.csdn.net/qq_34086980/article/details/105814036
今日推荐