返回一个”虚拟节点“,通常缩写为 VNode:一个普通对象,其中包含向 Vue 描述它应在页面上渲染哪种节点的信息,包括所有子节点的描述。它的目的是用于手动编写的渲染函数

  1. render() {
  2. return h('h1', {}, 'Some title')
  3. }

参数

接收三个参数:type,props 和 children

type

  • 类型:String | Object | Function
  • 详细:HTML 标签名、组件、异步组件或函数式组件。使用返回 null 的函数将渲染一个注释。此参数是必需的。

    props

  • 类型:Object

  • 详细:一个对象,与我们将在模板中使用的 attribute、prop 和事件相对应。可选。

    children

  • 类型:String | Array | Object

  • 详细:子代 VNode,使用 h() 生成,或者使用字符串来获取“文本 VNode”,或带有插槽的对象。可选。
    1. h('div', {}, [
    2. 'Some text comes first.',
    3. h('h1', 'A headline'),
    4. h(MyComponent, {
    5. someProp: 'foobar'
    6. })
    7. ])

    源码

    ```javascript // type 元素的类型 // propsOrChildren 数据对象, 这里主要表示(props, attrs, dom props, class 和 style) // children 子节点也是一个any类型

export function h(type: any, propsOrChildren?: any, children?: any): VNode { if (arguments.length === 2) { if (isObject(propsOrChildren) && !isArray(propsOrChildren)) { // single vnode without props if (isVNode(propsOrChildren)) { return createVNode(type, null, [propsOrChildren]) } // props without children return createVNode(type, propsOrChildren) } else { // omit props return createVNode(type, null, propsOrChildren) } } else { if (isVNode(children)) { children = [children] } return createVNode(type, propsOrChildren, children) } }

function createVNode( type: VNodeTypes | ClassComponent | typeof NULLDYNAMIC_COMPONENT, props: (Data & VNodeProps) | null = null, children: unknown = null, // 更新标志 patchFlag: number = 0, // 自定义属性 dynamicProps: string[] | null = null, // 是否是动态节点,(v-if v-for) isBlockNode = false ): VNode { // type必传参数 if (!type || type === NULL_DYNAMIC_COMPONENT) { if (__DEV && !type) { warn(Invalid vnode type when creating vnode: ${type}.) } type = Comment }

// Class 类型的type标准化 // class component normalization. if (isFunction(type) && ‘vccOpts’ in type) { type = type.vccOpts }

// class & style normalization. if (props) { // props 如果是响应式,clone 一个副本 if (isProxy(props) || InternalObjectKey in props) { props = extend({}, props) } let { class: klass, style } = props

  1. // 标准化class, 支持 string , array, object 三种形式
  2. if (klass && !isString(klass)) {
  3. props.class = normalizeClass(klass)
  4. }
  5. // 标准化style, 支持 array ,object 两种形式
  6. if (isObject(style)) {
  7. // reactive state objects need to be cloned since they are likely to be
  8. // mutated
  9. if (isProxy(style) && !isArray(style)) {
  10. style = extend({}, style)
  11. }
  12. props.style = normalizeStyle(style)
  13. }

}

// encode the vnode type information into a bitmap const shapeFlag = isString(type) ? ShapeFlags.ELEMENT : FEATURE_SUSPENSE && isSuspense(type) ? ShapeFlags.SUSPENSE : isTeleport(type) ? ShapeFlags.TELEPORT : isObject(type) ? ShapeFlags.STATEFUL_COMPONENT : isFunction(type) ? ShapeFlags.FUNCTIONAL_COMPONENT : 0

if (DEV && shapeFlag & ShapeFlags.STATEFUL_COMPONENT && isProxy(type)) { type = toRaw(type) warn( Vue received a Component which was made a reactive object. This can + lead to unnecessary performance overhead, and should be avoided by + marking the component with \markRaw` or using `shallowRef` +instead of `ref`.,\nComponent that was made reactive: `, type ) }

// 构造 VNode 模型 const vnode: VNode = { v_isVNode: true, v_skip: true, type, props, key: props && normalizeKey(props), ref: props && normalizeRef(props), scopeId: currentScopeId, children: null, component: null, suspense: null, dirs: null, transition: null, el: null, anchor: null, target: null, targetAnchor: null, staticCount: 0, shapeFlag, patchFlag, dynamicProps, dynamicChildren: null, appContext: null }

normalizeChildren(vnode, children)

// presence of a patch flag indicates this node needs patching on updates. // component nodes also should always be patched, because even if the // component doesn’t need to update, it needs to persist the instance on to // the next vnode so that it can be properly unmounted later.

// patchFlag 标志存在表示节点需要更新,组件节点一直存在 patchFlag,因为即使不需要更新,它需要将实例持久化到下一个 vnode,以便以后可以正确卸载它 if ( shouldTrack > 0 && !isBlockNode && currentBlock && // the EVENTS flag is only for hydration and if it is the only flag, the // vnode should not be considered dynamic due to handler caching. patchFlag !== PatchFlags.HYDRATE_EVENTS && (patchFlag > 0 || shapeFlag & ShapeFlags.SUSPENSE || shapeFlag & ShapeFlags.TELEPORT || shapeFlag & ShapeFlags.STATEFUL_COMPONENT || shapeFlag & ShapeFlags.FUNCTIONAL_COMPONENT) ) { // 压入 VNode 栈 currentBlock.push(vnode) }

return vnode } ```