current position:Home>Chapter 7 'vue3 runtimecore' - high level API`

Chapter 7 'vue3 runtimecore' - high level API`

2022-01-27 04:15:28 Jian Darui

Chapter 7 Vue3 RunTimeCore—— Higher order API

This article , From h Function introduced , Gradually understand hcreateVNodecloneVNodemergePropsisVNode Equal higher order API Usage and principle of .

Rendering function h()

stay Vue2 in , There's a big picture API:render function .Vue The internal returns a to this function h function , Used to create Vnode Description object of .

This time, , stay Vue3 in . take h Functions are independent , As a separate API, Its function remains the same : Used to create a description of the rendered node Vnode Describe objects .

Three parameters are acceptable : typepropschildren.

  • type Used to represent Vnode Node type , It can be HTML Tag name 、 Components 、 Asynchronous components or functional components . Use return null Functions that render a comment , This parameter must be passed .
  • props It's an object , With the attributeprop Corresponding to events . Optional .
  • children Is a child node VNode, Use h() Generate , Or use a string to get the “ Text VNode”, Or objects with slots . Optional .

At the beginning of learning Vue When , I always don't understand render Function h How to use . If you have been through HTML Template syntax to build page structure , It might be right h Functions are not particularly familiar , Now we can learn together .

When we create a component , It's usually passed HTML Template to describe UI part , such as :

  • Use HTML label :
<template>
    <input 
      type="radio"
      :id="branch"
      :value="branch"
      name="branch"
      v-model="currentBranch">

    <label :for="branch">{{ branch }}</label>
</template>
  • Use custom component labels :
<template>
   <tree-item class="item" :model="treeData" @chang="changeHandler"></tree-item>
</template>

In fact, these can be passed JS Abstract into three parts , And describe... With objects :

  • Used to represent the template label type type
  • To the template attributeprop And events
  • Label the child nodes of the package children

And the child nodes can also be abstracted into the same structure .

 Official picture
Official picture

and h Function does just one thing . Pass it on to him typepropschildren. It returns the corresponding Vnode Describe objects .

Can you directly create a Vnode Describe objects

Certainly. , But if it involves Vnode The description of all their own words , A little too tired , And it's easy to make mistakes .

Let's take a look first Vue Internally defined Vnode Object contains properties :

  • __v_isVNode: *true*, Internal attributes , The attribute is expressed as Vnode
  • __v_skip: true, Internal attributes , Indicates skipping the responsive conversion , reactive This attribute will be used for judgment during conversion
  • isCompatRoot?: *true*, Used to judge whether compatibility processing has been done
  • type: VNodeTypes, Type of virtual node
  • props: (VNodeProps & ExtraProps) | *null*, Virtual node props
  • key: *string* | *number* | *null*, Virtual phase key, Can be used for diff
  • ref: VNodeNormalizedRef | *null*, Reference to virtual phase
  • scopeId: *string* | *null*, Is limited to SFC( Single file component ), Set up currentRenderingInstance When the current instance is rendered , Phase I setup
  • slotScopeIds: *string*[] | *null*, Single file components only , Related to the slot of single file component
  • children: VNodeNormalizedChildren, Child node
  • component: ComponentInternalInstance | null, Component instance
  • dirs: DirectiveBinding[] | null, At present Vnode Bound instructions
  • transition: TransitionHooks<HostElement> | null, TransitionHooks
  • DOM Related properties
    • el: HostNode | *null*, Host stage
    • anchor: HostNode | *null* // fragment anchor
    • target: HostElement | *null* , teleport target The target of transmission
    • targetAnchor: HostNode | *null* // teleport target anchor
    • staticCount: *number*, Number of static nodes included
  • suspense Hang related properties
    • suspense: SuspenseBoundary | *null*

    • ssContent: VNode | *null*

    • ssFallback: VNode | *null*

  • optimization only Properties for optimization
    • shapeFlag: *number*
    • patchFlag: *number*
    • dynamicProps: *string*[] | *null*
    • dynamicChildren: VNode[] | *null*
  • The properties of the root node
    • appContext: AppContext | *null*, Instance context

You can see in the Vue Inside , For one Vnode There are about twenty attributes that describe an object , Some attributes must also be standardized .

Vue In order to reduce a certain burden , But not too closed , The rendering is created h. When users need it , adopt h Function to create the corresponding Vnode that will do .

This gives some high-level players a free play space .

Then why use h Function? ?

In fact, the official documents have given a very appropriate and simple example , Portal : Rendering function

Through official examples , You can know .

javascript Compared to template syntax , There's a higher degree of freedom . When using templates is too cumbersome , Like multiple if/else, You can use the rendering function h.

How to use

v-if

<span v-if="user">
   {{user.name}}
</span>
<p v-else>Plase login.</p>

send h The function is expressed as follows :

render() {
  return this.user ? h('span'null, user.name) : h('p''Plase login.')
}

You can know from the above code :

  • You can replace... With a ternary operator v-if/v-else Instructions
  • Or by if/else Instead of v-if/v-else Instructions

v-show

<div v-show="isActive">Content</div>

send h The function is expressed as follows :

render() {
    return h("div", {
      "directives": [{
        name"show",
        value: isActive
      }], 
    }, "Content");
}

v-for

<ul>
  <li v-for="item in items">{{ item.name }}</li>
</ul>

send h The function is expressed as follows :

render() {
    return h('ul'this.items.map((item) => {
      return h('li', item.name)
    }))
}
  • Can pass map Function instead of v-for Instructions
  • adopt map Back to Vnode, Each is a different object

v-on

<button @click="onClick">Button</button>

send h The function is expressed as follows :

render() {
    return h('button',  {
  onClick: onClick
 })
}

about input The label can go through

  • onBlur Listening for out of focus events

  • onFocus Listen for Focus Events

  • onInput Listen for input events

  • onClick Monitor click events

  • onKeypress Listening to keyboard events

v-model

stay Vue in , We can go through v-bind Transfer value from up to down .

It can also be done through v-model Transfer value from up to down .

When using v-model when , Its essence is v-bind And v-on The grammar sugar of ;

stay h Function , How to say v-model? Let's look at the code :

props: ['modelValue'],
emits: ['update:modelValue'],
render() {
  return h(Component, {
    modelValuethis.modelValue,
    'onUpdate:modelValue'value => this.$emit('update:modelValue', value)
  })
}

The above code is an official example . This means :

  • But use v-model binding value when . Must be given to sub components props Binding one in value, And a function to listen for updates , Instead of v-bind And v-on.

attrs

In English props And attrs All represent the meaning of attributes , But in Vue These two attributes have different meanings :

  • props Represents the attribute of the element object
  • attrs Represents the attribute of the element tag

For example, when we call h Function creation Vnode when , The second parameter passed , Namely Vnode Object properties .

And when we need to set the element label attrs What should I do when I'm in a hurry ?

<input type="button" disabled="true"/>

send h The function is expressed as follows :

render() {
    return h(input, {
     "attrs": {
         type: button,
         disabledtrue
     }
 })
}

Therefore, in h You can see in the function props contain attrs.

v-slot

stay Vue in slot Provides content distribution capabilities for templates .

When use , Just use slot The tag can be occupied .

Let's see how to use h Function to create a slot .

<div><slot></slot></div>

send h The function is expressed as follows :

Normal slot

Can pass this.$slots Access the contents of the static slot , Each slot is one VNode Array :

render() {
  return h('div', {}, this.$slots.default())
}

Scope slot :

<!-- Define slot components child-->
<div><slot :text="message"></slot></div>
<!-- Use components child-->
<div><child v-slot:default="slotProps">{{ slotProps.text }}</child></div>
render() {
  return h('div', {}, this.$slots.default({
    textthis.message
  }))
}
  • Can pass this.$slot Access the contents of the static slot
  • If you need to pass the status , You can give this.$slots.default() Function passes an object parameter

Custom components

<div><child v-slot:default="slotProps"><span>{{ slotProps.text }}</span></child></div>
const { h, resolveComponent } = Vue

render() {
  return h('div', [
    h(
      resolveComponent('child'),
      {},
      //  take  `slots`  With  { name: props => VNode | Array<VNode> }  Passed to child objects in the form of .
      {
        default(props) => Vue.h('span', props.text)
      }
    )
  ])
}

resolveComponent API Returns the child Component's Vnode.

Dynamic components

<component :is="name"></component>

send h The function is expressed as follows :

const { h, resolveDynamicComponent } = Vue
render() {
  const Component = resolveDynamicComponent(this.name)
  return h(Component)
}

When using dynamic components , have access to resolveDynamicComponent Instead of is attribute,resolveDynamicComponent Support passing a component name 、 One HTML Element name or a component option object .

Can give is Pass anything to resolveDynamicComponent What to pass on .

Built in components

stay Vue in , KeepAlive, Teleport, Transition, TransitionGroup Etc. is called Vue Built in components , The difference between built-in components and user-defined components is , Built in components are not registered locally or globally , So I can't get through resolveComponent Go visit them . In the use of h Function time , You need to be confident :

const { h, KeepAlive, Teleport, Transition, TransitionGroup } = Vue
// ...
render () {
  return h(Transition, { mode'out-in' }, /* ... */)
}

ref

<someComponent ref="someComponent"></someComponent>

send h The function is expressed as follows :

render() {
  return h(someComponent, {"ref""someComponent"}))
}

Custom instruction

have access to withDirectives Apply custom directives to VNode

const { h, resolveDirective, withDirectives } = Vue

// <div v-pin:top.animate="200"></div>
render () {
  const pin = resolveDirective('pin')
  return withDirectives(h('div'), [
    [pin, 200'top', { animatetrue }]
  ])
}

resolveDirective API Is the same function used to resolve the instruction name inside the template . Only if you have not directly accessed the definition object of the instruction , That's what we need to do .

Later, we'll talk about withDirectives API Analyze .

*

resolveComponent API And resolveDirective API The principle is the same . When we create a component , Configure this component compontes attribute 、directives attribute . These properties will eventually be bound to the instance ,resolve Components / Instruction process , By accessing the current instance compontes/directives The process of attributes .

*

Rendering function h() Source code analysis

*

This part may be boring , Mainly analysis h How functions are created Vnode, What will be done during the creation process .

*

Vnode It's an ordinary of a virtual node JS object ,Vue According to the object information , Render the corresponding node .

Vnode type

You can give h Function transitive Vnode Node type :

  • string
  • VNode
  • Component
  • Text
  • Static
  • Comment
  • Fragment
  • TeleportImpl
  • SuspenseImpl

Look directly at the source code :

function h(type, propsOrChildren, children{
  //  Judge whether there are child nodes according to the parameter length
  const l = arguments.length
  
  if (l === 2) {
    //  Pass two parameters
    if (isObject(propsOrChildren) && !isArray(propsOrChildren)) {
      // propsOrChildren  Is an object and not an array
      if (isVNode(propsOrChildren)) {
        //props yes Vnode type , be propsOrChildren Is a child node
        return createVNode(type, null, [propsOrChildren])
      }
      // props Does not contain child nodes
      return createVNode(type, propsOrChildren)
    } else {
      //  Omit props
      return createVNode(type, null, propsOrChildren)
    }
  } else {
    //  When it exists 2 When a parameter has been added
    //  Put the child nodes into children Array
    if (l > 3) {
      children = Array.prototype.slice.call(arguments2)
    } else if (l === 3 && isVNode(children)) {
      children = [children]
    }
    return createVNode(type, propsOrChildren, children)
  }
}

Through the above code , Rendering function h It's just createVnode Grammatical sugar of function .

Rendering h() The main responsibility of function is to judge the length and type of parameters , To call createVnode Function creation Vnode.

Look at the below createVnode function .

createVNode

createVnode Function in Vue Source code runtime-core in vnode.ts Folder .

createVNode In fact, it's called _createVNode.

*

There is no need to pay attention to vnodeArgsTransformer.

*
export const createVNode = (__DEV__ ? createVNodeWithArgsTransform : _createVNode)

const createVNodeWithArgsTransform = (...args) => {
  return _createVNode(
    ...(vnodeArgsTransformer
      ? vnodeArgsTransformer(args, currentRenderingInstance)
      : args)
  )
}


_createVNode

  • First, check the type , If not as expected , stay dev The environment will warn , prod The environment will be used as the annotation node type .
  • In judging whether it is already Vnode, If yes, clone the node directly , And sort out their own points .
  • If it's a class component , Will get __vccOpts
  • do Vue2 Asynchronous or functional component compatibility
  • If props There is , Would be right props Judge , And regulate what we pass to the node classstyle, Will class Process as string , take style Processing as an object
  • establish Vnode
  • Standardize and sort out sub nodes
  • If you need to do compatibility processing during construction , Then do Vue2 Compatible processing of , Finally, return to the virtual node
function _createVNode(
  type,
  props,
  children,
  patchFlag,
  dynamicProps,
  isBlockNode = false
)
{
   
  if (!type || type === NULL_DYNAMIC_COMPONENT) {
    if (__DEV__ && !type) {
      warn(`Invalid vnode type when creating vnode: ${type}.`)
    }
    type = Comment
  }

  //  If type yes Vnode type , Then clone this type of node , Standardize and sort out sub nodes , Returns the cloned node
  if (isVNode(type)) {
    const cloned = cloneVNode(type, props, true /* mergeRef: true */)
    if (children) {
      normalizeChildren(cloned, children)
    }
    return cloned
  }

  //  If the class component type
  if (isClassComponent(type)) {
    type = type.__vccOpts
  }

  //  compatible Vue2 To deal with
  if (__COMPAT__) {
    type = convertLegacyComponent(type, currentRenderingInstance)
  }

  // if Block mainly deals with  class & style  attribute
  if (props) {
    // for reactive or proxy objects, we need to clone it to enable mutation.
    //  If props It's a reactive object , Need to pass through Object.assign Copy
    if (isProxy(props) || InternalObjectKey in props) {
      props = extend({}, props)
    }
    let { class: klass, style } = props
    if (klass && !isString(klass)) {
      // class It's not a string , It needs to be a string
      props.class = normalizeClass(klass)
    }
    if (isObject(style)) {
      // reactive state objects need to be cloned since they are likely to be
      // mutated
      if (isProxy(style) && !isArray(style)) {
        style = extend({}, style)
      }
      props.style = normalizeStyle(style)
    }
  }

  //  take vnode The type information is changed to  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)) {
    //  Omit ...
    )
  }

  //  establish VNode Description object of
  const vnode: VNode = {
    __v_isVNodetrue//  identification   The object is a virtual node
    __v_skiptrue//  identification   This object skips proxy
    type, //  type
    props,
    key: props && normalizeKey(props), //  carding props
    ref: props && normalizeRef(props),//  carding ref
    scopeId: currentScopeId,
    slotScopeIdsnull,
    childrennull,
    componentnull,
    suspensenull,
    ssContentnull,
    ssFallbacknull,
    dirsnull,
    transitionnull,
    elnull,
    anchornull,
    targetnull,
    targetAnchornull,
    staticCount0,
    shapeFlag,
    patchFlag,
    dynamicProps,
    dynamicChildrennull//  Dynamic child nodes
    appContextnull //  Instance context
  }

  // validate key
  if (__DEV__ && vnode.key !== vnode.key) {
    warn(`VNode created with invalid key (NaN). VNode type:`, vnode.type)
  }
      
  //  Canonical child nodes
  normalizeChildren(vnode, children)

  //  If the time is suspense Type virtual DOM, standard  suspense  Child node
  if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
    ;(type).normalize(vnode)
  }

  //  I don't care about
  if (
    isBlockTreeEnabled > 0 &&
    !isBlockNode &&
    currentBlock &&
    (patchFlag > 0 || shapeFlag & ShapeFlags.COMPONENT) &&
    patchFlag !== PatchFlags.HYDRATE_EVENTS
  ) {
    currentBlock.push(vnode)
  }
  //  Compatible processing
  if (__COMPAT__) {
    convertLegacyVModelProps(vnode)
    convertLegacyRefInFor(vnode)
    defineLegacyVNodeProperties(vnode)
  }
  //  Return to the virtual node
  return vnode
}

You can see from the above code that ,_createVNode The main responsibilities of the function :

  • Carding specification props Medium classstylechild
  • establish Vnode Description object of , And back to
  • Yes Vue2 Do compatible processing
*

Object.assign And Proxy:https://stackoverflow.com/questions/43185453/object-assign-and-proxies

*

In the above code , If type yes Vnode type , Would call cloneVNode Create a cloned node , Let's take a look cloneVNode function .

cloneVNode

In fact, we can think about it first , Clone a Vnode, In fact, it can be simplified to clone a tree tree, Is a recursive cloning process .

export function cloneVNode(
  vnode,
  extraProps,
  mergeRef = false
)
{
  // This is intentionally NOT using spread or extend to avoid the runtime
  // key enumeration cost.
  const { props, ref, patchFlag, children } = vnode
  //  Merge props
  const mergedProps = extraProps ? mergeProps(props || {}, extraProps) : props
  //  establish Vnode Clone objects
  const cloned = {
    __v_isVNodetrue,
    __v_skiptrue,
    type: vnode.type,
    props: mergedProps,
    key: mergedProps && normalizeKey(mergedProps),
    ref:
      extraProps && extraProps.ref
          mergeRef && ref
          ? isArray(ref)
            ? ref.concat(normalizeRef(extraProps)!)
            : [ref, normalizeRef(extraProps)!]
          : normalizeRef(extraProps)
        : ref,
    scopeId: vnode.scopeId,
    slotScopeIds: vnode.slotScopeIds,
    children:
      __DEV__ && patchFlag === PatchFlags.HOISTED && isArray(children)
        ? children.map(deepCloneVNode) //  Deep clone the child nodes
        : children,
    target: vnode.target,
    targetAnchor: vnode.targetAnchor,
    staticCount: vnode.staticCount,
    shapeFlag: vnode.shapeFlag,
    patchFlag:
      extraProps && vnode.type !== Fragment
        ? patchFlag === -1 // hoisted node
          ? PatchFlags.FULL_PROPS
          : patchFlag | PatchFlags.FULL_PROPS
        : patchFlag,
    dynamicProps: vnode.dynamicProps,
    dynamicChildren: vnode.dynamicChildren,
    appContext: vnode.appContext,
    dirs: vnode.dirs,
    transition: vnode.transition,

    component: vnode.component,
    suspense: vnode.suspense,
    ssContent: vnode.ssContent && cloneVNode(vnode.ssContent),
    ssFallback: vnode.ssFallback && cloneVNode(vnode.ssFallback),
    el: vnode.el,
    anchor: vnode.anchor
  }
  //  Compatible processing
  if (__COMPAT__) {
    defineLegacyVNodeProperties(cloned)
  }
  return cloned
}

cloneVNode I have done so many things :

  • Merge props
  • Create clone object
  • Yes Vnode Deep cloning of child nodes

deepClone

Deep cloning , If the child node is an array type, recursive cloning will be performed .

function deepCloneVNode(vnode{
  const cloned = cloneVNode(vnode)
  if (isArray(vnode.children)) {
    cloned.children = vnode.children.map(deepCloneVNode)
  }
  return cloned
}

isVNode

It's simple , According to create Vnode Private attribute judgment when describing objects .

export function isVNode(value{
  return value ? value.__v_isVNode === true : false
}

normalizeChildren

stay _createVNode in , We know , If Vnode Purely at child nodes , Will execute normalizeChildren Standardize and sort out sub nodes . Look at the below normalizeChildren how :

export function normalizeChildren(vnode, children{
  let type = 0
  const { shapeFlag } = vnode
  //  The following will be true of children Judge by type , Different types of , Different operating
  if (children == null) {
    // children  yes null
    children = null
  } else if (isArray(children)) {
    // children  It's an array , Mark type by ARRAY_CHILDREN
    type = ShapeFlags.ARRAY_CHILDREN
  } else if (typeof children === 'object') {
     // children  It's the object
    if (shapeFlag & ShapeFlags.ELEMENT || shapeFlag & ShapeFlags.TELEPORT) {
      // Normalize slot to plain children for plain element and Teleport
      const slot = (children).default
      if (slot) {
        // _c marker is added by withCtx() indicating this is a compiled slot
        slot._c && (slot._d = false)
        normalizeChildren(vnode, slot())
        slot._c && (slot._d = true)
      }
      return
    } else {
      type = ShapeFlags.SLOTS_CHILDREN
      const slotFlag = (children)._
      if (!slotFlag && !(InternalObjectKey in children!)) {
         
        ;(children)._ctx = currentRenderingInstance
      } else if (slotFlag === SlotFlags.FORWARDED && currentRenderingInstance) {
        
        if (
          (currentRenderingInstance.slots)._ === SlotFlags.STABLE
        ) {
          ;(children)._ = SlotFlags.STABLE
        } else {
          ;(children)._ = SlotFlags.DYNAMIC
          vnode.patchFlag |= PatchFlags.DYNAMIC_SLOTS
        }
      }
    }
  } else if (isFunction(children)) {
    // children Is the function
    children = { default: children, _ctx: currentRenderingInstance }
    type = ShapeFlags.SLOTS_CHILDREN
  } else {
    children = String(children)
    // force teleport children to array so it can be moved around
    if (shapeFlag & ShapeFlags.TELEPORT) {
      type = ShapeFlags.ARRAY_CHILDREN
      children = [createTextVNode(children)]
    } else {
      type = ShapeFlags.TEXT_CHILDREN
    }
  }
  vnode.children = children
  vnode.shapeFlag |= type
}

As you can see from the code above ,normalizeChildren Mainly for Vnode.children And type Made a normative comb .

isClassComponent

export function isClassComponent(value{
  return isFunction(value) && '__vccOpts' in value
}

normalizeStyle

When we bind components style When , Maybe it's like this :

<div :style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
data() {
  return {
    activeColor'red',
    fontSize30
  }
}

Dynamically bind through object syntax style.

It may also be written like this :

<div :style="[baseStyles, overridingStyles]"></div>
data() {
  return {
      baseStyles: {
          activeColor'red',
       fontSize30
      },
      overridingStyles: {
       display: flex
      },
  }
}

Bind multiple elements through an array style object .

But these two ways of writing . In the end, it will pass normalizeStyle Function to sort out the specification .

Look at the below normalizeStyle function :

export function normalizeStyle(value{
  if (isArray(value)) {
    const res = {}
    for (let i = 0; i < value.length; i++) {
      const item = value[i]
      const normalized = normalizeStyle(
        isString(item) ? parseStringStyle(item) : item
      )
      if (normalized) {
        for (const key in normalized) {
          res[key] = normalized[key]
        }
      }
    }
    return res
  } else if (isObject(value)) {
    return value
  }
}

normalizeStyle The function is very simple , By traversing recursively, the array type value, The specification is the object type and returns .

normalizeClass

When we bind classes to nodes , There are basically three forms :

  • Bind as a string
  • Bind as an object
  • Bind as an array

But eventually bound to the node class, with string Handle ,normalizeClass That's what I did .

Will all not string The form of link is string.

export function normalizeClass(value{
  let res = ''
  if (isString(value)) {
    res = value
  } else if (isArray(value)) {
    //  Traversal recursive processing
    for (let i = 0; i < value.length; i++) {
      const normalized = normalizeClass(value[i])
      if (normalized) {
        res += normalized + ' '
      }
    }
  } else if (isObject(value)) {
    //  Convert objects to string
    for (const name in value) {
      if (value[name]) {
        res += name + ' '
      }
    }
  }
  return res.trim()
}

normalizeClass The idea of function is actually the same as normalizeStyle identical .

*

Tip: This kind of traversal recursion often appears in interview questions .

*

mergeProps

In the previous analysis , We know , clone Vnode In the process of , Call back mergeProps Yes vnode.props A merger . And will merge the mergedProps Pass to cloned Vnode.

Look at the below mergedProps How to merge ?

export function mergeProps(...args{
  const ret = extend({}, args[0])
  for (let i = 1; i < args.length; i++) {
    const toMerge = args[i]
    for (const key in toMerge) {
      if (key === 'class') {
        // merge Class
        if (ret.class !== toMerge.class) {
          ret.class = normalizeClass([ret.class, toMerge.class])
        }
      } else if (key === 'style') {
        // merge Style
        ret.style = normalizeStyle([ret.style, toMerge.style])
      } else if (isOn(key)) {
        // merge  Listening events
        const existing = ret[key]
        const incoming = toMerge[key]
        if (existing !== incoming) {
          ret[key] = existing
            ? [].concat(existing, incoming)
            : incoming
        }
      } else if (key !== '') {
        ret[key] = toMerge[key]
      }
    }
  }
  return ret
}

  • It will be for the nodes classstyle、 Combine bound events and non empty attributes
  • The process of merging will affect classstyle do normalize Handle
  • If you bind multiple events , All events will be stored in the array

withDirectives

function withDirectives(vnode, directives{
  const internalInstance = currentRenderingInstance
  if (internalInstance === null) {
    __DEV__ && warn(`withDirectives can only be used inside render functions.`)
    return vnode
  }
  const instance = internalInstance.proxy
  const bindings = vnode.dirs || (vnode.dirs = [])
  for (let i = 0; i < directives.length; i++) {
    let [dir, value, arg, modifiers] = directives[i]
    if (isFunction(dir)) {
      dir = {
        mounted: dir,
        updated: dir
      }
    }
    bindings.push({
      dir,
      instance,
      value,
      oldValue,
      arg,
      modifiers
    })
  }
  return vnode
}

From the above code, we can see ,withDirectives API It's a simple idea , Is to configure objects by traversing instructions , Instructions to be configured push to binding Collection .

summary

h The function is actually createVNode The grammar sugar of , What's back is a Js Common object . stay createVNode API Creating Vnode When , Would be right Vnode Of props、childrenrefclassstyle And other attributes . If Type Is directly Vnode type , The of deep cloning will be returned Vnode object . Compare with HTML Template syntax , Use h Function to create components Vnode, More flexible , And more abstract .

Reference resources :

[ Official documents ](

copyright notice
author[Jian Darui],Please bring the original link to reprint, thank you.
https://en.cdmana.com/2022/01/202201270415127114.html

Random recommended