Skip to content

h函数

vnode数据结构

type

vnode存在很多种不同的节点类型

  • string:DOM 节点
js
const vnode1 = h('div', {
    class: 'test'
  }, 'hello render')

// vnode1
{
  	// 用于判断是否为vnode的标识
    "__v_isVNode": true,
    // 节点类型 	
    "type": "div",
    // 属性
    "props": {
        "class": "test"	
    },
    // 类型标识
    "shapeFlag": 9,
    // 子节点
    "children": "hello render"
}
const vnode1 = h('div', {
    class: 'test'
  }, 'hello render')

// vnode1
{
  	// 用于判断是否为vnode的标识
    "__v_isVNode": true,
    // 节点类型 	
    "type": "div",
    // 属性
    "props": {
        "class": "test"	
    },
    // 类型标识
    "shapeFlag": 9,
    // 子节点
    "children": "hello render"
}
  • object:组件
js
const component = {
    render() {
      const vnode1 = h('div', '这是一个 component')
      return vnode1
    }
  }
const vnode2 = h(component)

// vnode2
{
    "__v_isVNode": true,
    "type": { render: f },
    "shapeFlag": 4,
    "children": null
}
const component = {
    render() {
      const vnode1 = h('div', '这是一个 component')
      return vnode1
    }
  }
const vnode2 = h(component)

// vnode2
{
    "__v_isVNode": true,
    "type": { render: f },
    "shapeFlag": 4,
    "children": null
}
  • Text:文本节点
  • Comment:注释节点
  • Fragment:包含多个根节点的模板被表示为一个片段 (fragment)
js
const { h, render, Text, Comment, Fragment } = Vue
  const vnodeText = h(Text, '这是一个 Text')
  console.log(vnodeText);

  const vnodeComment = h(Comment, '这是一个 Comment')
  console.log(vnodeComment);

  const vnodeFragment = h(Fragment, '这是一个 Fragment')
  console.log(vnodeFragment);

// vnodeText
{
    "__v_isVNode": true,
    "props": null,
    "type": Symbol(Text),
    "shapeFlag": 8,
    "children": "这是一个 Text"
}

// vnodeComment
{
    "__v_isVNode": true,
    "props": null,
    "type": Symbol(Comment),
    "shapeFlag": 8,
    "children": "这是一个 Comment"
}

// vnodeFragment
{
    "__v_isVNode": true,
    "props": null,
     "type": Symbol(Fragment),
    "shapeFlag": 8,
    "children": "这是一个 Fragment"
}
const { h, render, Text, Comment, Fragment } = Vue
  const vnodeText = h(Text, '这是一个 Text')
  console.log(vnodeText);

  const vnodeComment = h(Comment, '这是一个 Comment')
  console.log(vnodeComment);

  const vnodeFragment = h(Fragment, '这是一个 Fragment')
  console.log(vnodeFragment);

// vnodeText
{
    "__v_isVNode": true,
    "props": null,
    "type": Symbol(Text),
    "shapeFlag": 8,
    "children": "这是一个 Text"
}

// vnodeComment
{
    "__v_isVNode": true,
    "props": null,
    "type": Symbol(Comment),
    "shapeFlag": 8,
    "children": "这是一个 Comment"
}

// vnodeFragment
{
    "__v_isVNode": true,
    "props": null,
     "type": Symbol(Fragment),
    "shapeFlag": 8,
    "children": "这是一个 Fragment"
}

shapeFlag

类型标识包含两个信息:自己和children的类型

js
export const enum ShapeFlags {
    /**
     * type = Element
     */
    ELEMENT = 1,
    /**
     * 函数组件
     */
    FUNCTIONAL_COMPONENT = 1 << 1,
    /**
     * 有状态(响应数据)组件
     */
    STATEFUL_COMPONENT = 1 << 2,
    /**
     * children = Text
     */
    TEXT_CHILDREN = 1 << 3,
    /**
     * children = Array
     */
    ARRAY_CHILDREN = 1 << 4,
    /**
     * children = slot
     */
    SLOTS_CHILDREN = 1 << 5,
    /**
     * 组件:有状态(响应数据)组件 | 函数组件
     */
    COMPONENT = ShapeFlags.STATEFUL_COMPONENT | ShapeFlags.FUNCTIONAL_COMPONENT
}
export const enum ShapeFlags {
    /**
     * type = Element
     */
    ELEMENT = 1,
    /**
     * 函数组件
     */
    FUNCTIONAL_COMPONENT = 1 << 1,
    /**
     * 有状态(响应数据)组件
     */
    STATEFUL_COMPONENT = 1 << 2,
    /**
     * children = Text
     */
    TEXT_CHILDREN = 1 << 3,
    /**
     * children = Array
     */
    ARRAY_CHILDREN = 1 << 4,
    /**
     * children = slot
     */
    SLOTS_CHILDREN = 1 << 5,
    /**
     * 组件:有状态(响应数据)组件 | 函数组件
     */
    COMPONENT = ShapeFlags.STATEFUL_COMPONENT | ShapeFlags.FUNCTIONAL_COMPONENT
}

比如 Element + TEXT_CHILDREN,那么 shapeFlag = ShapeFlags.ELEMENT | ShapeFlags.TEXT_CHILDREN

实现

h -> createVNode -> createBaseVNode

  • h函数:处理不同的参数,统一后调用createVNode
js
function h(type: any, propsOrChildren?: any, children?: any): VNode {
    // 获取用户传递的参数数量
    const l = arguments.length
    // 如果用户只传递了两个参数,那么证明第二个参数可能是 props , 也可能是 children
    if (l === 2) {
        // 如果 第二个参数是对象,但不是数组。则第二个参数只有两种可能性:1. VNode 2.普通的 props
        if (isObject(propsOrChildren) && !isArray(propsOrChildren)) {
            // 如果是 VNode,则 第二个参数代表了 children
            if (isVNode(propsOrChildren)) {
                return createVNode(type, null, [propsOrChildren])
            }
            // 如果不是 VNode, 则第二个参数代表了 props
            return createVNode(type, propsOrChildren)
        }
        // 如果第二个参数不是单纯的 object,则 第二个参数代表了 props
        else {
            return createVNode(type, null, propsOrChildren)
        }
    }
    // 如果用户传递了三个或以上的参数,那么证明第二个参数一定代表了 props
    else {
        // 如果参数在三个以上,则从第二个参数开始,把后续所有参数都作为 children
        if (l > 3) {
            children = Array.prototype.slice.call(arguments, 2)
        }
        // 如果传递的参数只有三个,则 children 是单纯的 children
        else if (l === 3 && isVNode(children)) {
            children = [children]
        }
        // 触发 createVNode 方法,创建 VNode 实例
        return createVNode(type, propsOrChildren, children)
    }
}
function h(type: any, propsOrChildren?: any, children?: any): VNode {
    // 获取用户传递的参数数量
    const l = arguments.length
    // 如果用户只传递了两个参数,那么证明第二个参数可能是 props , 也可能是 children
    if (l === 2) {
        // 如果 第二个参数是对象,但不是数组。则第二个参数只有两种可能性:1. VNode 2.普通的 props
        if (isObject(propsOrChildren) && !isArray(propsOrChildren)) {
            // 如果是 VNode,则 第二个参数代表了 children
            if (isVNode(propsOrChildren)) {
                return createVNode(type, null, [propsOrChildren])
            }
            // 如果不是 VNode, 则第二个参数代表了 props
            return createVNode(type, propsOrChildren)
        }
        // 如果第二个参数不是单纯的 object,则 第二个参数代表了 props
        else {
            return createVNode(type, null, propsOrChildren)
        }
    }
    // 如果用户传递了三个或以上的参数,那么证明第二个参数一定代表了 props
    else {
        // 如果参数在三个以上,则从第二个参数开始,把后续所有参数都作为 children
        if (l > 3) {
            children = Array.prototype.slice.call(arguments, 2)
        }
        // 如果传递的参数只有三个,则 children 是单纯的 children
        else if (l === 3 && isVNode(children)) {
            children = [children]
        }
        // 触发 createVNode 方法,创建 VNode 实例
        return createVNode(type, propsOrChildren, children)
    }
}
  • createVNode:参数固定,生成一个 VNode 对象,并返回
js
/**
 * 生成一个 VNode 对象,并返回
 * @param type vnode.type string
 * @param props 标签属性或自定义属性 object
 * @param children 子节点 array
 * @returns vnode 对象
 */
export function createVNode(type, props, children?): VNode {
    // 通过 bit 位处理 shapeFlag 类型
    const shapeFlag = isString(type)
        ? ShapeFlags.ELEMENT
        : isObject(type)
        ? ShapeFlags.STATEFUL_COMPONENT
        : 0

    if (props) {
        // 处理 class
        let { class: klass, style } = props
        if (klass && !isString(klass)) {
            props.class = normalizeClass(klass)
        }
    }

    return createBaseVNode(type, props, children, shapeFlag)
}
/**
 * 生成一个 VNode 对象,并返回
 * @param type vnode.type string
 * @param props 标签属性或自定义属性 object
 * @param children 子节点 array
 * @returns vnode 对象
 */
export function createVNode(type, props, children?): VNode {
    // 通过 bit 位处理 shapeFlag 类型
    const shapeFlag = isString(type)
        ? ShapeFlags.ELEMENT
        : isObject(type)
        ? ShapeFlags.STATEFUL_COMPONENT
        : 0

    if (props) {
        // 处理 class
        let { class: klass, style } = props
        if (klass && !isString(klass)) {
            props.class = normalizeClass(klass)
        }
    }

    return createBaseVNode(type, props, children, shapeFlag)
}
  • createBaseVNode
js
/**
 * 构建基础 vnode
 */
function createBaseVNode(type, props, children, shapeFlag) {
    const vnode = {
        __v_isVNode: true,
        type,
        props,
        shapeFlag
    } as VNode

    // 根据children的类型修改vnode.shapeFlag
    normalizeChildren(vnode, children)

    return vnode
}
/**
 * 构建基础 vnode
 */
function createBaseVNode(type, props, children, shapeFlag) {
    const vnode = {
        __v_isVNode: true,
        type,
        props,
        shapeFlag
    } as VNode

    // 根据children的类型修改vnode.shapeFlag
    normalizeChildren(vnode, children)

    return vnode
}