| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910 |
- import {
- EMPTY_ARR,
- PatchFlags,
- ShapeFlags,
- SlotFlags,
- extend,
- isArray,
- isFunction,
- isObject,
- isOn,
- isString,
- normalizeClass,
- normalizeStyle,
- } from '@vue/shared'
- import type { Data } from '@vue/runtime-shared'
- import {
- type ClassComponent,
- type Component,
- type ComponentInternalInstance,
- type ConcreteComponent,
- isClassComponent,
- } from './component'
- import type { RawSlots } from './componentSlots'
- import {
- type ReactiveFlags,
- type Ref,
- isProxy,
- isRef,
- toRaw,
- } from '@vue/reactivity'
- import type { AppContext } from './apiCreateApp'
- import {
- type Suspense,
- type SuspenseBoundary,
- type SuspenseImpl,
- isSuspense,
- } from './components/Suspense'
- import type { DirectiveBinding } from './directives'
- import {
- type TransitionHooks,
- setTransitionHooks,
- } from './components/BaseTransition'
- import { warn } from './warning'
- import {
- type Teleport,
- type TeleportImpl,
- isTeleport,
- } from './components/Teleport'
- import {
- currentRenderingInstance,
- currentScopeId,
- } from './componentRenderContext'
- import type { RendererElement, RendererNode } from './renderer'
- import { NULL_DYNAMIC_COMPONENT } from './helpers/resolveAssets'
- import { hmrDirtyComponents } from './hmr'
- import { convertLegacyComponent } from './compat/component'
- import { convertLegacyVModelProps } from './compat/componentVModel'
- import { defineLegacyVNodeProperties } from './compat/renderFn'
- import { ErrorCodes, callWithAsyncErrorHandling } from './errorHandling'
- import type { ComponentPublicInstance } from './componentPublicInstance'
- import { isInternalObject } from './internalObject'
- export const Fragment = Symbol.for('v-fgt') as any as {
- __isFragment: true
- new (): {
- $props: VNodeProps
- }
- }
- export const Text: unique symbol = Symbol.for('v-txt')
- export const Comment: unique symbol = Symbol.for('v-cmt')
- export const Static: unique symbol = Symbol.for('v-stc')
- export type VNodeTypes =
- | string
- | VNode
- | Component
- | typeof Text
- | typeof Static
- | typeof Comment
- | typeof Fragment
- | typeof Teleport
- | typeof TeleportImpl
- | typeof Suspense
- | typeof SuspenseImpl
- export type VNodeRef =
- | string
- | Ref
- | ((
- ref: Element | ComponentPublicInstance | null,
- refs: Record<string, any>,
- ) => void)
- export type VNodeNormalizedRefAtom = {
- /**
- * component instance
- */
- i: ComponentInternalInstance
- /**
- * Actual ref
- */
- r: VNodeRef
- /**
- * setup ref key
- */
- k?: string
- /**
- * refInFor marker
- */
- f?: boolean
- }
- export type VNodeNormalizedRef =
- | VNodeNormalizedRefAtom
- | VNodeNormalizedRefAtom[]
- type VNodeMountHook = (vnode: VNode) => void
- type VNodeUpdateHook = (vnode: VNode, oldVNode: VNode) => void
- export type VNodeHook =
- | VNodeMountHook
- | VNodeUpdateHook
- | VNodeMountHook[]
- | VNodeUpdateHook[]
- // https://github.com/microsoft/TypeScript/issues/33099
- export type VNodeProps = {
- key?: PropertyKey
- ref?: VNodeRef
- ref_for?: boolean
- ref_key?: string
- // vnode hooks
- onVnodeBeforeMount?: VNodeMountHook | VNodeMountHook[]
- onVnodeMounted?: VNodeMountHook | VNodeMountHook[]
- onVnodeBeforeUpdate?: VNodeUpdateHook | VNodeUpdateHook[]
- onVnodeUpdated?: VNodeUpdateHook | VNodeUpdateHook[]
- onVnodeBeforeUnmount?: VNodeMountHook | VNodeMountHook[]
- onVnodeUnmounted?: VNodeMountHook | VNodeMountHook[]
- }
- type VNodeChildAtom =
- | VNode
- | string
- | number
- | boolean
- | null
- | undefined
- | void
- export type VNodeArrayChildren = Array<VNodeArrayChildren | VNodeChildAtom>
- export type VNodeChild = VNodeChildAtom | VNodeArrayChildren
- export type VNodeNormalizedChildren =
- | string
- | VNodeArrayChildren
- | RawSlots
- | null
- export interface VNode<
- HostNode = RendererNode,
- HostElement = RendererElement,
- ExtraProps = { [key: string]: any },
- > {
- /**
- * @internal
- */
- __v_isVNode: true
- /**
- * @internal
- */
- [ReactiveFlags.SKIP]: true
- type: VNodeTypes
- props: (VNodeProps & ExtraProps) | null
- key: PropertyKey | null
- ref: VNodeNormalizedRef | null
- /**
- * SFC only. This is assigned on vnode creation using currentScopeId
- * which is set alongside currentRenderingInstance.
- */
- scopeId: string | null
- /**
- * SFC only. This is assigned to:
- * - Slot fragment vnodes with :slotted SFC styles.
- * - Component vnodes (during patch/hydration) so that its root node can
- * inherit the component's slotScopeIds
- * @internal
- */
- slotScopeIds: string[] | null
- children: VNodeNormalizedChildren
- component: ComponentInternalInstance | null
- dirs: DirectiveBinding[] | null
- transition: TransitionHooks<HostElement> | null
- // DOM
- el: HostNode | null
- anchor: HostNode | null // fragment anchor
- target: HostElement | null // teleport target
- targetStart: HostNode | null // teleport target start anchor
- targetAnchor: HostNode | null // teleport target anchor
- /**
- * number of elements contained in a static vnode
- * @internal
- */
- staticCount: number
- // suspense
- suspense: SuspenseBoundary | null
- /**
- * @internal
- */
- ssContent: VNode | null
- /**
- * @internal
- */
- ssFallback: VNode | null
- // optimization only
- shapeFlag: number
- patchFlag: number
- /**
- * @internal
- */
- dynamicProps: string[] | null
- /**
- * @internal
- */
- dynamicChildren: (VNode[] & { hasOnce?: boolean }) | null
- // application root node only
- appContext: AppContext | null
- /**
- * @internal lexical scope owner instance
- */
- ctx: ComponentInternalInstance | null
- /**
- * @internal attached by v-memo
- */
- memo?: any[]
- /**
- * @internal index for cleaning v-memo cache
- */
- cacheIndex?: number
- /**
- * @internal __COMPAT__ only
- */
- isCompatRoot?: true
- /**
- * @internal custom element interception hook
- */
- ce?: (instance: ComponentInternalInstance) => void
- }
- // Since v-if and v-for are the two possible ways node structure can dynamically
- // change, once we consider v-if branches and each v-for fragment a block, we
- // can divide a template into nested blocks, and within each block the node
- // structure would be stable. This allows us to skip most children diffing
- // and only worry about the dynamic nodes (indicated by patch flags).
- export const blockStack: VNode['dynamicChildren'][] = []
- export let currentBlock: VNode['dynamicChildren'] = null
- /**
- * Open a block.
- * This must be called before `createBlock`. It cannot be part of `createBlock`
- * because the children of the block are evaluated before `createBlock` itself
- * is called. The generated code typically looks like this:
- *
- * ```js
- * function render() {
- * return (openBlock(),createBlock('div', null, [...]))
- * }
- * ```
- * disableTracking is true when creating a v-for fragment block, since a v-for
- * fragment always diffs its children.
- *
- * @private
- */
- export function openBlock(disableTracking = false): void {
- blockStack.push((currentBlock = disableTracking ? null : []))
- }
- export function closeBlock(): void {
- blockStack.pop()
- currentBlock = blockStack[blockStack.length - 1] || null
- }
- // Whether we should be tracking dynamic child nodes inside a block.
- // Only tracks when this value is > 0
- // We are not using a simple boolean because this value may need to be
- // incremented/decremented by nested usage of v-once (see below)
- export let isBlockTreeEnabled = 1
- /**
- * Block tracking sometimes needs to be disabled, for example during the
- * creation of a tree that needs to be cached by v-once. The compiler generates
- * code like this:
- *
- * ``` js
- * _cache[1] || (
- * setBlockTracking(-1),
- * _cache[1] = createVNode(...),
- * setBlockTracking(1),
- * _cache[1]
- * )
- * ```
- *
- * @private
- */
- export function setBlockTracking(value: number): void {
- isBlockTreeEnabled += value
- if (value < 0 && currentBlock) {
- // mark current block so it doesn't take fast path and skip possible
- // nested components duriung unmount
- currentBlock.hasOnce = true
- }
- }
- function setupBlock(vnode: VNode) {
- // save current block children on the block vnode
- vnode.dynamicChildren =
- isBlockTreeEnabled > 0 ? currentBlock || (EMPTY_ARR as any) : null
- // close block
- closeBlock()
- // a block is always going to be patched, so track it as a child of its
- // parent block
- if (isBlockTreeEnabled > 0 && currentBlock) {
- currentBlock.push(vnode)
- }
- return vnode
- }
- /**
- * @private
- */
- export function createElementBlock(
- type: string | typeof Fragment,
- props?: Record<string, any> | null,
- children?: any,
- patchFlag?: number,
- dynamicProps?: string[],
- shapeFlag?: number,
- ): VNode {
- return setupBlock(
- createBaseVNode(
- type,
- props,
- children,
- patchFlag,
- dynamicProps,
- shapeFlag,
- true /* isBlock */,
- ),
- )
- }
- /**
- * Create a block root vnode. Takes the same exact arguments as `createVNode`.
- * A block root keeps track of dynamic nodes within the block in the
- * `dynamicChildren` array.
- *
- * @private
- */
- export function createBlock(
- type: VNodeTypes | ClassComponent,
- props?: Record<string, any> | null,
- children?: any,
- patchFlag?: number,
- dynamicProps?: string[],
- ): VNode {
- return setupBlock(
- createVNode(
- type,
- props,
- children,
- patchFlag,
- dynamicProps,
- true /* isBlock: prevent a block from tracking itself */,
- ),
- )
- }
- export function isVNode(value: any): value is VNode {
- return value ? value.__v_isVNode === true : false
- }
- export function isSameVNodeType(n1: VNode, n2: VNode): boolean {
- if (__DEV__ && n2.shapeFlag & ShapeFlags.COMPONENT && n1.component) {
- const dirtyInstances = hmrDirtyComponents.get(n2.type as ConcreteComponent)
- if (dirtyInstances && dirtyInstances.has(n1.component)) {
- // #7042, ensure the vnode being unmounted during HMR
- // bitwise operations to remove keep alive flags
- n1.shapeFlag &= ~ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE
- n2.shapeFlag &= ~ShapeFlags.COMPONENT_KEPT_ALIVE
- // HMR only: if the component has been hot-updated, force a reload.
- return false
- }
- }
- return n1.type === n2.type && n1.key === n2.key
- }
- let vnodeArgsTransformer:
- | ((
- args: Parameters<typeof _createVNode>,
- instance: ComponentInternalInstance | null,
- ) => Parameters<typeof _createVNode>)
- | undefined
- /**
- * Internal API for registering an arguments transform for createVNode
- * used for creating stubs in the test-utils
- * It is *internal* but needs to be exposed for test-utils to pick up proper
- * typings
- */
- export function transformVNodeArgs(
- transformer?: typeof vnodeArgsTransformer,
- ): void {
- vnodeArgsTransformer = transformer
- }
- const createVNodeWithArgsTransform = (
- ...args: Parameters<typeof _createVNode>
- ): VNode => {
- return _createVNode(
- ...(vnodeArgsTransformer
- ? vnodeArgsTransformer(args, currentRenderingInstance)
- : args),
- )
- }
- const normalizeKey = ({ key }: VNodeProps): VNode['key'] =>
- key != null ? key : null
- const normalizeRef = ({
- ref,
- ref_key,
- ref_for,
- }: VNodeProps): VNodeNormalizedRefAtom | null => {
- if (typeof ref === 'number') {
- ref = '' + ref
- }
- return (
- ref != null
- ? isString(ref) || isRef(ref) || isFunction(ref)
- ? { i: currentRenderingInstance, r: ref, k: ref_key, f: !!ref_for }
- : ref
- : null
- ) as any
- }
- function createBaseVNode(
- type: VNodeTypes | ClassComponent | typeof NULL_DYNAMIC_COMPONENT,
- props: (Data & VNodeProps) | null = null,
- children: unknown = null,
- patchFlag = 0,
- dynamicProps: string[] | null = null,
- shapeFlag: number = type === Fragment ? 0 : ShapeFlags.ELEMENT,
- isBlockNode = false,
- needFullChildrenNormalization = false,
- ): VNode {
- const vnode = {
- __v_isVNode: true,
- __v_skip: true,
- type,
- props,
- key: props && normalizeKey(props),
- ref: props && normalizeRef(props),
- scopeId: currentScopeId,
- slotScopeIds: null,
- children,
- component: null,
- suspense: null,
- ssContent: null,
- ssFallback: null,
- dirs: null,
- transition: null,
- el: null,
- anchor: null,
- target: null,
- targetStart: null,
- targetAnchor: null,
- staticCount: 0,
- shapeFlag,
- patchFlag,
- dynamicProps,
- dynamicChildren: null,
- appContext: null,
- ctx: currentRenderingInstance,
- } as VNode
- if (needFullChildrenNormalization) {
- normalizeChildren(vnode, children)
- // normalize suspense children
- if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
- ;(type as typeof SuspenseImpl).normalize(vnode)
- }
- } else if (children) {
- // compiled element vnode - if children is passed, only possible types are
- // string or Array.
- vnode.shapeFlag |= isString(children)
- ? ShapeFlags.TEXT_CHILDREN
- : ShapeFlags.ARRAY_CHILDREN
- }
- // validate key
- if (__DEV__ && vnode.key !== vnode.key) {
- warn(`VNode created with invalid key (NaN). VNode type:`, vnode.type)
- }
- // track vnode for block tree
- if (
- isBlockTreeEnabled > 0 &&
- // avoid a block node from tracking itself
- !isBlockNode &&
- // has current parent block
- currentBlock &&
- // 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.
- (vnode.patchFlag > 0 || shapeFlag & ShapeFlags.COMPONENT) &&
- // 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.
- vnode.patchFlag !== PatchFlags.NEED_HYDRATION
- ) {
- currentBlock.push(vnode)
- }
- if (__COMPAT__) {
- convertLegacyVModelProps(vnode)
- defineLegacyVNodeProperties(vnode)
- }
- return vnode
- }
- export { createBaseVNode as createElementVNode }
- export const createVNode = (
- __DEV__ ? createVNodeWithArgsTransform : _createVNode
- ) as typeof _createVNode
- function _createVNode(
- type: VNodeTypes | ClassComponent | typeof NULL_DYNAMIC_COMPONENT,
- props: (Data & VNodeProps) | null = null,
- children: unknown = null,
- patchFlag: number = 0,
- dynamicProps: string[] | null = null,
- isBlockNode = false,
- ): VNode {
- if (!type || type === NULL_DYNAMIC_COMPONENT) {
- if (__DEV__ && !type) {
- warn(`Invalid vnode type when creating vnode: ${type}.`)
- }
- type = Comment
- }
- if (isVNode(type)) {
- // createVNode receiving an existing vnode. This happens in cases like
- // <component :is="vnode"/>
- // #2078 make sure to merge refs during the clone instead of overwriting it
- const cloned = cloneVNode(type, props, true /* mergeRef: true */)
- if (children) {
- normalizeChildren(cloned, children)
- }
- if (isBlockTreeEnabled > 0 && !isBlockNode && currentBlock) {
- if (cloned.shapeFlag & ShapeFlags.COMPONENT) {
- currentBlock[currentBlock.indexOf(type)] = cloned
- } else {
- currentBlock.push(cloned)
- }
- }
- cloned.patchFlag = PatchFlags.BAIL
- return cloned
- }
- // class component normalization.
- if (isClassComponent(type)) {
- type = type.__vccOpts
- }
- // 2.x async/functional component compat
- if (__COMPAT__) {
- type = convertLegacyComponent(type, currentRenderingInstance)
- }
- // class & style normalization.
- if (props) {
- // for reactive or proxy objects, we need to clone it to enable mutation.
- props = guardReactiveProps(props)!
- let { class: klass, style } = props
- if (klass && !isString(klass)) {
- 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)
- }
- }
- // 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 that 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,
- )
- }
- return createBaseVNode(
- type,
- props,
- children,
- patchFlag,
- dynamicProps,
- shapeFlag,
- isBlockNode,
- true,
- )
- }
- export function guardReactiveProps(
- props: (Data & VNodeProps) | null,
- ): (Data & VNodeProps) | null {
- if (!props) return null
- return isProxy(props) || isInternalObject(props) ? extend({}, props) : props
- }
- export function cloneVNode<T, U>(
- vnode: VNode<T, U>,
- extraProps?: (Data & VNodeProps) | null,
- mergeRef = false,
- cloneTransition = false,
- ): VNode<T, U> {
- // This is intentionally NOT using spread or extend to avoid the runtime
- // key enumeration cost.
- const { props, ref, patchFlag, children, transition } = vnode
- const mergedProps = extraProps ? mergeProps(props || {}, extraProps) : props
- const cloned: VNode<T, U> = {
- __v_isVNode: true,
- __v_skip: true,
- type: vnode.type,
- props: mergedProps,
- key: mergedProps && normalizeKey(mergedProps),
- ref:
- extraProps && extraProps.ref
- ? // #2078 in the case of <component :is="vnode" ref="extra"/>
- // if the vnode itself already has a ref, cloneVNode will need to merge
- // the refs so the single vnode can be set on multiple refs
- mergeRef && ref
- ? isArray(ref)
- ? ref.concat(normalizeRef(extraProps)!)
- : [ref, normalizeRef(extraProps)!]
- : normalizeRef(extraProps)
- : ref,
- scopeId: vnode.scopeId,
- slotScopeIds: vnode.slotScopeIds,
- children:
- __DEV__ && patchFlag === PatchFlags.CACHED && isArray(children)
- ? (children as VNode[]).map(deepCloneVNode)
- : children,
- target: vnode.target,
- targetStart: vnode.targetStart,
- targetAnchor: vnode.targetAnchor,
- staticCount: vnode.staticCount,
- shapeFlag: vnode.shapeFlag,
- // if the vnode is cloned with extra props, we can no longer assume its
- // existing patch flag to be reliable and need to add the FULL_PROPS flag.
- // note: preserve flag for fragments since they use the flag for children
- // fast paths only.
- patchFlag:
- extraProps && vnode.type !== Fragment
- ? patchFlag === PatchFlags.CACHED // hoisted node
- ? PatchFlags.FULL_PROPS
- : patchFlag | PatchFlags.FULL_PROPS
- : patchFlag,
- dynamicProps: vnode.dynamicProps,
- dynamicChildren: vnode.dynamicChildren,
- appContext: vnode.appContext,
- dirs: vnode.dirs,
- transition,
- // These should technically only be non-null on mounted VNodes. However,
- // they *should* be copied for kept-alive vnodes. So we just always copy
- // them since them being non-null during a mount doesn't affect the logic as
- // they will simply be overwritten.
- component: vnode.component,
- suspense: vnode.suspense,
- ssContent: vnode.ssContent && cloneVNode(vnode.ssContent),
- ssFallback: vnode.ssFallback && cloneVNode(vnode.ssFallback),
- el: vnode.el,
- anchor: vnode.anchor,
- ctx: vnode.ctx,
- ce: vnode.ce,
- }
- // if the vnode will be replaced by the cloned one, it is necessary
- // to clone the transition to ensure that the vnode referenced within
- // the transition hooks is fresh.
- if (transition && cloneTransition) {
- setTransitionHooks(
- cloned as VNode,
- transition.clone(cloned as VNode) as TransitionHooks,
- )
- }
- if (__COMPAT__) {
- defineLegacyVNodeProperties(cloned as VNode)
- }
- return cloned
- }
- /**
- * Dev only, for HMR of hoisted vnodes reused in v-for
- * https://github.com/vitejs/vite/issues/2022
- */
- function deepCloneVNode(vnode: VNode): VNode {
- const cloned = cloneVNode(vnode)
- if (isArray(vnode.children)) {
- cloned.children = (vnode.children as VNode[]).map(deepCloneVNode)
- }
- return cloned
- }
- /**
- * @private
- */
- export function createTextVNode(text: string = ' ', flag: number = 0): VNode {
- return createVNode(Text, null, text, flag)
- }
- /**
- * @private
- */
- export function createStaticVNode(
- content: string,
- numberOfNodes: number,
- ): VNode {
- // A static vnode can contain multiple stringified elements, and the number
- // of elements is necessary for hydration.
- const vnode = createVNode(Static, null, content)
- vnode.staticCount = numberOfNodes
- return vnode
- }
- /**
- * @private
- */
- export function createCommentVNode(
- text: string = '',
- // when used as the v-else branch, the comment node must be created as a
- // block to ensure correct updates.
- asBlock: boolean = false,
- ): VNode {
- return asBlock
- ? (openBlock(), createBlock(Comment, null, text))
- : createVNode(Comment, null, text)
- }
- export function normalizeVNode(child: VNodeChild): VNode {
- if (child == null || typeof child === 'boolean') {
- // empty placeholder
- return createVNode(Comment)
- } else if (isArray(child)) {
- // fragment
- return createVNode(
- Fragment,
- null,
- // #3666, avoid reference pollution when reusing vnode
- child.slice(),
- )
- } else if (isVNode(child)) {
- // already vnode, this should be the most common since compiled templates
- // always produce all-vnode children arrays
- return cloneIfMounted(child)
- } else {
- // strings and numbers
- return createVNode(Text, null, String(child))
- }
- }
- // optimized normalization for template-compiled render fns
- export function cloneIfMounted(child: VNode): VNode {
- return (child.el === null && child.patchFlag !== PatchFlags.CACHED) ||
- child.memo
- ? child
- : cloneVNode(child)
- }
- export function normalizeChildren(vnode: VNode, children: unknown): void {
- let type = 0
- const { shapeFlag } = vnode
- if (children == null) {
- children = null
- } else if (isArray(children)) {
- type = ShapeFlags.ARRAY_CHILDREN
- } else if (typeof children === 'object') {
- if (shapeFlag & (ShapeFlags.ELEMENT | ShapeFlags.TELEPORT)) {
- // Normalize slot to plain children for plain element and Teleport
- const slot = (children as any).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 as RawSlots)._
- if (!slotFlag && !isInternalObject(children)) {
- // if slots are not normalized, attach context instance
- // (compiled / normalized slots already have context)
- ;(children as RawSlots)._ctx = currentRenderingInstance
- } else if (slotFlag === SlotFlags.FORWARDED && currentRenderingInstance) {
- // a child component receives forwarded slots from the parent.
- // its slot type is determined by its parent's slot type.
- if (
- (currentRenderingInstance.slots as RawSlots)._ === SlotFlags.STABLE
- ) {
- ;(children as RawSlots)._ = SlotFlags.STABLE
- } else {
- ;(children as RawSlots)._ = SlotFlags.DYNAMIC
- vnode.patchFlag |= PatchFlags.DYNAMIC_SLOTS
- }
- }
- }
- } else if (isFunction(children)) {
- 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 as string)]
- } else {
- type = ShapeFlags.TEXT_CHILDREN
- }
- }
- vnode.children = children as VNodeNormalizedChildren
- vnode.shapeFlag |= type
- }
- export function mergeProps(...args: (Data & VNodeProps)[]): Data {
- const ret: Data = {}
- for (let i = 0; i < args.length; i++) {
- const toMerge = args[i]
- for (const key in toMerge) {
- if (key === 'class') {
- if (ret.class !== toMerge.class) {
- ret.class = normalizeClass([ret.class, toMerge.class])
- }
- } else if (key === 'style') {
- ret.style = normalizeStyle([ret.style, toMerge.style])
- } else if (isOn(key)) {
- const existing = ret[key]
- const incoming = toMerge[key]
- if (
- incoming &&
- existing !== incoming &&
- !(isArray(existing) && existing.includes(incoming))
- ) {
- ret[key] = existing
- ? [].concat(existing as any, incoming as any)
- : incoming
- }
- } else if (key !== '') {
- ret[key] = toMerge[key]
- }
- }
- }
- return ret
- }
- export function invokeVNodeHook(
- hook: VNodeHook,
- instance: ComponentInternalInstance | null,
- vnode: VNode,
- prevVNode: VNode | null = null,
- ): void {
- callWithAsyncErrorHandling(hook, instance, ErrorCodes.VNODE_HOOK, [
- vnode,
- prevVNode,
- ])
- }
|