2019-08-23 02:07:51 +00:00
|
|
|
import {
|
|
|
|
isArray,
|
|
|
|
isFunction,
|
|
|
|
isString,
|
|
|
|
isObject,
|
|
|
|
EMPTY_ARR,
|
2020-01-27 22:23:42 +00:00
|
|
|
extend,
|
|
|
|
normalizeClass,
|
2020-02-13 22:47:00 +00:00
|
|
|
normalizeStyle,
|
2020-02-14 06:36:42 +00:00
|
|
|
PatchFlags,
|
|
|
|
ShapeFlags
|
2019-08-23 02:07:51 +00:00
|
|
|
} from '@vue/shared'
|
2019-10-08 13:26:09 +00:00
|
|
|
import {
|
|
|
|
ComponentInternalInstance,
|
|
|
|
Data,
|
2020-03-12 15:46:32 +00:00
|
|
|
Component,
|
|
|
|
ClassComponent
|
2019-10-08 13:26:09 +00:00
|
|
|
} from './component'
|
2019-05-31 10:07:43 +00:00
|
|
|
import { RawSlots } from './componentSlots'
|
2020-04-15 20:45:20 +00:00
|
|
|
import { isProxy, Ref, toRaw } from '@vue/reactivity'
|
2019-12-22 17:25:04 +00:00
|
|
|
import { AppContext } from './apiCreateApp'
|
2020-02-15 16:40:09 +00:00
|
|
|
import {
|
|
|
|
SuspenseImpl,
|
|
|
|
isSuspense,
|
|
|
|
SuspenseBoundary
|
|
|
|
} from './components/Suspense'
|
2019-10-26 20:00:07 +00:00
|
|
|
import { DirectiveBinding } from './directives'
|
2019-11-24 21:00:46 +00:00
|
|
|
import { TransitionHooks } from './components/BaseTransition'
|
|
|
|
import { warn } from './warning'
|
2019-12-16 18:33:10 +00:00
|
|
|
import { currentScopeId } from './helpers/scopeId'
|
2020-03-31 14:52:42 +00:00
|
|
|
import { TeleportImpl, isTeleport } from './components/Teleport'
|
2020-03-16 16:20:52 +00:00
|
|
|
import { currentRenderingInstance } from './componentRenderUtils'
|
2020-03-23 15:08:22 +00:00
|
|
|
import { RendererNode, RendererElement } from './renderer'
|
2020-04-24 19:06:21 +00:00
|
|
|
import { NULL_DYNAMIC_COMPONENT } from './helpers/resolveAssets'
|
2019-05-28 02:28:25 +00:00
|
|
|
|
2019-11-02 02:54:01 +00:00
|
|
|
export const Fragment = (Symbol(__DEV__ ? 'Fragment' : undefined) as any) as {
|
|
|
|
__isFragment: true
|
2019-11-04 23:38:55 +00:00
|
|
|
new (): {
|
|
|
|
$props: VNodeProps
|
|
|
|
}
|
2019-11-02 02:54:01 +00:00
|
|
|
}
|
2019-10-26 14:51:55 +00:00
|
|
|
export const Text = Symbol(__DEV__ ? 'Text' : undefined)
|
|
|
|
export const Comment = Symbol(__DEV__ ? 'Comment' : undefined)
|
2020-02-12 16:56:42 +00:00
|
|
|
export const Static = Symbol(__DEV__ ? 'Static' : undefined)
|
2018-09-19 15:35:38 +00:00
|
|
|
|
2019-08-23 19:27:17 +00:00
|
|
|
export type VNodeTypes =
|
2019-05-25 15:51:20 +00:00
|
|
|
| string
|
2019-10-08 13:26:09 +00:00
|
|
|
| Component
|
2019-05-25 15:51:20 +00:00
|
|
|
| typeof Text
|
2020-02-12 16:56:42 +00:00
|
|
|
| typeof Static
|
2019-09-24 18:37:14 +00:00
|
|
|
| typeof Comment
|
2020-02-15 16:40:09 +00:00
|
|
|
| typeof Fragment
|
2020-03-31 14:52:42 +00:00
|
|
|
| typeof TeleportImpl
|
2019-11-04 23:38:55 +00:00
|
|
|
| typeof SuspenseImpl
|
2018-09-19 15:35:38 +00:00
|
|
|
|
2020-03-16 16:40:58 +00:00
|
|
|
export type VNodeRef =
|
|
|
|
| string
|
|
|
|
| Ref
|
|
|
|
| ((ref: object | null, refs: Record<string, any>) => void)
|
|
|
|
|
|
|
|
export type VNodeNormalizedRef = [ComponentInternalInstance, VNodeRef]
|
|
|
|
|
2020-03-18 13:51:17 +00:00
|
|
|
type VNodeMountHook = (vnode: VNode) => void
|
|
|
|
type VNodeUpdateHook = (vnode: VNode, oldVNode: VNode) => void
|
2020-03-18 15:43:32 +00:00
|
|
|
export type VNodeHook =
|
|
|
|
| VNodeMountHook
|
|
|
|
| VNodeUpdateHook
|
|
|
|
| VNodeMountHook[]
|
|
|
|
| VNodeUpdateHook[]
|
2020-03-18 13:51:17 +00:00
|
|
|
|
2019-11-01 13:58:27 +00:00
|
|
|
export interface VNodeProps {
|
|
|
|
[key: string]: any
|
|
|
|
key?: string | number
|
2020-03-16 16:40:58 +00:00
|
|
|
ref?: VNodeRef
|
2019-11-20 23:04:44 +00:00
|
|
|
|
|
|
|
// vnode hooks
|
2020-03-18 15:43:32 +00:00
|
|
|
onVnodeBeforeMount?: VNodeMountHook | VNodeMountHook[]
|
|
|
|
onVnodeMounted?: VNodeMountHook | VNodeMountHook[]
|
|
|
|
onVnodeBeforeUpdate?: VNodeUpdateHook | VNodeUpdateHook[]
|
|
|
|
onVnodeUpdated?: VNodeUpdateHook | VNodeUpdateHook[]
|
|
|
|
onVnodeBeforeUnmount?: VNodeMountHook | VNodeMountHook[]
|
|
|
|
onVnodeUnmounted?: VNodeMountHook | VNodeMountHook[]
|
2019-11-01 13:58:27 +00:00
|
|
|
}
|
|
|
|
|
2020-05-01 14:37:40 +00:00
|
|
|
type VNodeChildAtom =
|
|
|
|
| VNode
|
|
|
|
| string
|
|
|
|
| number
|
|
|
|
| boolean
|
|
|
|
| null
|
|
|
|
| undefined
|
|
|
|
| void
|
2019-09-06 20:58:32 +00:00
|
|
|
|
2020-05-01 14:37:40 +00:00
|
|
|
export interface VNodeArrayChildren
|
|
|
|
extends Array<VNodeArrayChildren | VNodeChildAtom> {}
|
2018-10-12 23:49:41 +00:00
|
|
|
|
2020-03-23 15:08:22 +00:00
|
|
|
export type VNodeChild = VNodeChildAtom | VNodeArrayChildren
|
2019-09-06 20:58:32 +00:00
|
|
|
|
2020-03-23 15:08:22 +00:00
|
|
|
export type VNodeNormalizedChildren =
|
2019-09-06 20:58:32 +00:00
|
|
|
| string
|
2020-03-23 15:08:22 +00:00
|
|
|
| VNodeArrayChildren
|
2019-09-06 20:58:32 +00:00
|
|
|
| RawSlots
|
|
|
|
| null
|
2019-05-31 10:07:43 +00:00
|
|
|
|
2020-03-23 15:08:22 +00:00
|
|
|
export interface VNode<HostNode = RendererNode, HostElement = RendererElement> {
|
2019-09-30 14:45:50 +00:00
|
|
|
_isVNode: true
|
2019-05-25 15:51:20 +00:00
|
|
|
type: VNodeTypes
|
2019-11-01 13:58:27 +00:00
|
|
|
props: VNodeProps | null
|
2019-05-25 15:51:20 +00:00
|
|
|
key: string | number | null
|
2020-03-16 16:40:58 +00:00
|
|
|
ref: VNodeNormalizedRef | null
|
2019-12-16 18:33:10 +00:00
|
|
|
scopeId: string | null // SFC only
|
2020-03-23 15:08:22 +00:00
|
|
|
children: VNodeNormalizedChildren
|
2019-09-06 16:58:31 +00:00
|
|
|
component: ComponentInternalInstance | null
|
2020-03-23 15:08:22 +00:00
|
|
|
suspense: SuspenseBoundary | null
|
2019-10-26 20:00:07 +00:00
|
|
|
dirs: DirectiveBinding[] | null
|
2019-11-22 22:10:17 +00:00
|
|
|
transition: TransitionHooks | null
|
2019-05-28 05:27:31 +00:00
|
|
|
|
|
|
|
// DOM
|
2019-05-28 09:19:47 +00:00
|
|
|
el: HostNode | null
|
|
|
|
anchor: HostNode | null // fragment anchor
|
2020-03-31 14:52:42 +00:00
|
|
|
target: HostElement | null // teleport target
|
|
|
|
targetAnchor: HostNode | null // teleport target anchor
|
2019-05-28 05:27:31 +00:00
|
|
|
|
|
|
|
// optimization only
|
2019-06-02 08:35:19 +00:00
|
|
|
shapeFlag: number
|
2019-06-01 09:43:41 +00:00
|
|
|
patchFlag: number
|
2019-05-25 15:51:20 +00:00
|
|
|
dynamicProps: string[] | null
|
|
|
|
dynamicChildren: VNode[] | null
|
2019-09-02 20:09:34 +00:00
|
|
|
|
|
|
|
// application root node only
|
|
|
|
appContext: AppContext | null
|
2018-10-12 23:49:41 +00:00
|
|
|
}
|
|
|
|
|
2019-05-30 13:24:40 +00:00
|
|
|
// 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).
|
2019-05-27 05:48:40 +00:00
|
|
|
const blockStack: (VNode[] | null)[] = []
|
2019-10-21 15:30:45 +00:00
|
|
|
let currentBlock: VNode[] | null = null
|
2018-09-19 15:35:38 +00:00
|
|
|
|
2020-05-01 14:37:40 +00:00
|
|
|
/**
|
|
|
|
* 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 fragment block, since a fragment
|
|
|
|
* always diffs its children.
|
|
|
|
*
|
|
|
|
* @internal
|
|
|
|
*/
|
2020-02-10 23:19:49 +00:00
|
|
|
export function openBlock(disableTracking = false) {
|
2019-10-21 15:30:45 +00:00
|
|
|
blockStack.push((currentBlock = disableTracking ? null : []))
|
2019-05-25 15:51:20 +00:00
|
|
|
}
|
2018-10-12 23:49:41 +00:00
|
|
|
|
2019-10-23 21:57:40 +00:00
|
|
|
// 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)
|
|
|
|
let shouldTrack = 1
|
|
|
|
|
2020-04-30 21:04:35 +00:00
|
|
|
/**
|
|
|
|
* 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]
|
|
|
|
* )
|
|
|
|
* ```
|
2020-05-01 14:37:40 +00:00
|
|
|
*
|
2020-04-30 21:04:35 +00:00
|
|
|
* @internal
|
|
|
|
*/
|
2019-10-23 21:57:40 +00:00
|
|
|
export function setBlockTracking(value: number) {
|
|
|
|
shouldTrack += value
|
|
|
|
}
|
2018-10-12 23:49:41 +00:00
|
|
|
|
2020-05-01 14:37:40 +00:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* @internal
|
|
|
|
*/
|
2019-05-25 15:51:20 +00:00
|
|
|
export function createBlock(
|
2020-03-12 15:46:32 +00:00
|
|
|
type: VNodeTypes | ClassComponent,
|
2019-05-25 15:51:20 +00:00
|
|
|
props?: { [key: string]: any } | null,
|
|
|
|
children?: any,
|
|
|
|
patchFlag?: number,
|
|
|
|
dynamicProps?: string[]
|
|
|
|
): VNode {
|
2020-04-24 16:18:51 +00:00
|
|
|
const vnode = createVNode(
|
|
|
|
type,
|
|
|
|
props,
|
|
|
|
children,
|
|
|
|
patchFlag,
|
|
|
|
dynamicProps,
|
|
|
|
true /* isBlock: prevent a block from tracking itself */
|
|
|
|
)
|
2019-10-21 15:30:45 +00:00
|
|
|
// save current block children on the block vnode
|
|
|
|
vnode.dynamicChildren = currentBlock || EMPTY_ARR
|
|
|
|
// close block
|
|
|
|
blockStack.pop()
|
|
|
|
currentBlock = blockStack[blockStack.length - 1] || null
|
|
|
|
// a block is always going to be patched, so track it as a child of its
|
|
|
|
// parent block
|
2020-03-18 22:14:51 +00:00
|
|
|
if (currentBlock) {
|
2019-10-21 15:30:45 +00:00
|
|
|
currentBlock.push(vnode)
|
|
|
|
}
|
2019-05-25 15:51:20 +00:00
|
|
|
return vnode
|
2018-10-12 17:42:19 +00:00
|
|
|
}
|
|
|
|
|
2019-10-10 14:17:16 +00:00
|
|
|
export function isVNode(value: any): value is VNode {
|
2019-09-30 14:52:50 +00:00
|
|
|
return value ? value._isVNode === true : false
|
2019-09-12 03:44:37 +00:00
|
|
|
}
|
|
|
|
|
2019-11-20 23:04:44 +00:00
|
|
|
export function isSameVNodeType(n1: VNode, n2: VNode): boolean {
|
2019-12-12 23:13:59 +00:00
|
|
|
if (
|
|
|
|
__DEV__ &&
|
|
|
|
n2.shapeFlag & ShapeFlags.COMPONENT &&
|
|
|
|
(n2.type as Component).__hmrUpdated
|
|
|
|
) {
|
|
|
|
// HMR only: if the component has been hot-updated, force a reload.
|
|
|
|
return false
|
|
|
|
}
|
2019-11-20 23:04:44 +00:00
|
|
|
return n1.type === n2.type && n1.key === n2.key
|
|
|
|
}
|
|
|
|
|
2020-03-23 20:54:28 +00:00
|
|
|
let vnodeArgsTransformer:
|
|
|
|
| ((
|
|
|
|
args: Parameters<typeof _createVNode>,
|
|
|
|
instance: ComponentInternalInstance | null
|
|
|
|
) => Parameters<typeof _createVNode>)
|
|
|
|
| undefined
|
|
|
|
|
2020-04-30 21:04:35 +00:00
|
|
|
/**
|
|
|
|
* Internal API for registering an arguments transform for createVNode
|
|
|
|
* used for creating stubs in the test-utils
|
|
|
|
* @internal
|
|
|
|
*/
|
2020-03-23 20:54:28 +00:00
|
|
|
export function transformVNodeArgs(transformer?: typeof vnodeArgsTransformer) {
|
|
|
|
vnodeArgsTransformer = transformer
|
|
|
|
}
|
|
|
|
|
|
|
|
const createVNodeWithArgsTransform = (
|
|
|
|
...args: Parameters<typeof _createVNode>
|
|
|
|
): VNode => {
|
|
|
|
return _createVNode(
|
|
|
|
...(vnodeArgsTransformer
|
|
|
|
? vnodeArgsTransformer(args, currentRenderingInstance)
|
|
|
|
: args)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2020-04-15 20:45:20 +00:00
|
|
|
export const InternalObjectKey = `__vInternal`
|
2020-04-06 21:57:27 +00:00
|
|
|
|
2020-04-24 16:56:38 +00:00
|
|
|
const normalizeKey = ({ key }: VNodeProps): VNode['key'] =>
|
|
|
|
key != null ? key : null
|
|
|
|
|
|
|
|
const normalizeRef = ({ ref }: VNodeProps): VNode['ref'] =>
|
|
|
|
(ref != null
|
|
|
|
? isArray(ref)
|
|
|
|
? ref
|
|
|
|
: [currentRenderingInstance!, ref]
|
|
|
|
: null) as any
|
|
|
|
|
2020-04-14 22:32:27 +00:00
|
|
|
export const createVNode = (__DEV__
|
2020-03-23 21:36:19 +00:00
|
|
|
? createVNodeWithArgsTransform
|
|
|
|
: _createVNode) as typeof _createVNode
|
2020-03-23 20:54:28 +00:00
|
|
|
|
|
|
|
function _createVNode(
|
2020-04-24 19:06:21 +00:00
|
|
|
type: VNodeTypes | ClassComponent | typeof NULL_DYNAMIC_COMPONENT,
|
2019-11-20 23:04:44 +00:00
|
|
|
props: (Data & VNodeProps) | null = null,
|
2019-10-01 01:17:12 +00:00
|
|
|
children: unknown = null,
|
2019-06-01 09:43:41 +00:00
|
|
|
patchFlag: number = 0,
|
2020-04-24 16:18:51 +00:00
|
|
|
dynamicProps: string[] | null = null,
|
|
|
|
isBlockNode = false
|
2019-05-25 15:51:20 +00:00
|
|
|
): VNode {
|
2020-04-24 19:06:21 +00:00
|
|
|
if (!type || type === NULL_DYNAMIC_COMPONENT) {
|
|
|
|
if (__DEV__ && !type) {
|
2020-03-15 15:29:21 +00:00
|
|
|
warn(`Invalid vnode type when creating vnode: ${type}.`)
|
2020-03-11 20:44:14 +00:00
|
|
|
}
|
2019-11-24 21:00:46 +00:00
|
|
|
type = Comment
|
|
|
|
}
|
|
|
|
|
2020-03-12 15:46:32 +00:00
|
|
|
// class component normalization.
|
|
|
|
if (isFunction(type) && '__vccOpts' in type) {
|
|
|
|
type = type.__vccOpts
|
|
|
|
}
|
|
|
|
|
2019-08-23 02:07:51 +00:00
|
|
|
// class & style normalization.
|
2020-03-18 22:14:51 +00:00
|
|
|
if (props) {
|
2019-08-23 02:07:51 +00:00
|
|
|
// for reactive or proxy objects, we need to clone it to enable mutation.
|
2020-04-15 20:45:20 +00:00
|
|
|
if (isProxy(props) || InternalObjectKey in props) {
|
2019-08-23 02:07:51 +00:00
|
|
|
props = extend({}, props)
|
|
|
|
}
|
2019-10-21 15:30:45 +00:00
|
|
|
let { class: klass, style } = props
|
2020-03-18 22:14:51 +00:00
|
|
|
if (klass && !isString(klass)) {
|
2019-10-21 15:30:45 +00:00
|
|
|
props.class = normalizeClass(klass)
|
2019-08-23 02:07:51 +00:00
|
|
|
}
|
2020-01-29 20:10:45 +00:00
|
|
|
if (isObject(style)) {
|
2019-08-23 02:07:51 +00:00
|
|
|
// reactive state objects need to be cloned since they are likely to be
|
|
|
|
// mutated
|
2020-04-15 20:45:20 +00:00
|
|
|
if (isProxy(style) && !isArray(style)) {
|
2019-08-23 02:07:51 +00:00
|
|
|
style = extend({}, style)
|
|
|
|
}
|
|
|
|
props.style = normalizeStyle(style)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-02 14:22:44 +00:00
|
|
|
// encode the vnode type information into a bitmap
|
2019-08-22 15:12:37 +00:00
|
|
|
const shapeFlag = isString(type)
|
|
|
|
? ShapeFlags.ELEMENT
|
2020-02-15 16:40:09 +00:00
|
|
|
: __FEATURE_SUSPENSE__ && isSuspense(type)
|
2019-10-29 16:30:09 +00:00
|
|
|
? ShapeFlags.SUSPENSE
|
2020-03-31 14:52:42 +00:00
|
|
|
: isTeleport(type)
|
|
|
|
? ShapeFlags.TELEPORT
|
2020-02-15 16:40:09 +00:00
|
|
|
: isObject(type)
|
|
|
|
? ShapeFlags.STATEFUL_COMPONENT
|
|
|
|
: isFunction(type)
|
|
|
|
? ShapeFlags.FUNCTIONAL_COMPONENT
|
|
|
|
: 0
|
2019-06-02 11:40:50 +00:00
|
|
|
|
2020-04-15 20:45:20 +00:00
|
|
|
if (__DEV__ && shapeFlag & ShapeFlags.STATEFUL_COMPONENT && isProxy(type)) {
|
2020-04-14 22:07:47 +00:00
|
|
|
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 ` +
|
2020-04-15 20:45:20 +00:00
|
|
|
`marking the component with \`markRaw\` or using \`shallowRef\` ` +
|
2020-04-14 22:07:47 +00:00
|
|
|
`instead of \`ref\`.`,
|
|
|
|
`\nComponent that was made reactive: `,
|
|
|
|
type
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2019-05-25 15:51:20 +00:00
|
|
|
const vnode: VNode = {
|
2019-09-30 14:45:50 +00:00
|
|
|
_isVNode: true,
|
2019-05-25 15:51:20 +00:00
|
|
|
type,
|
2019-06-01 09:43:41 +00:00
|
|
|
props,
|
2020-04-24 16:56:38 +00:00
|
|
|
key: props && normalizeKey(props),
|
|
|
|
ref: props && normalizeRef(props),
|
2019-12-16 18:33:10 +00:00
|
|
|
scopeId: currentScopeId,
|
2019-06-02 14:22:44 +00:00
|
|
|
children: null,
|
2019-05-28 05:27:31 +00:00
|
|
|
component: null,
|
2019-09-07 15:28:40 +00:00
|
|
|
suspense: null,
|
2019-10-26 20:00:07 +00:00
|
|
|
dirs: null,
|
2019-11-20 23:04:44 +00:00
|
|
|
transition: null,
|
2019-05-27 05:48:40 +00:00
|
|
|
el: null,
|
|
|
|
anchor: null,
|
2019-05-29 08:10:25 +00:00
|
|
|
target: null,
|
2020-03-28 03:12:52 +00:00
|
|
|
targetAnchor: null,
|
2019-08-22 15:12:37 +00:00
|
|
|
shapeFlag,
|
2019-05-25 15:51:20 +00:00
|
|
|
patchFlag,
|
|
|
|
dynamicProps,
|
2019-09-02 20:09:34 +00:00
|
|
|
dynamicChildren: null,
|
|
|
|
appContext: null
|
2018-09-19 15:35:38 +00:00
|
|
|
}
|
2019-06-01 09:43:41 +00:00
|
|
|
|
2019-06-02 14:22:44 +00:00
|
|
|
normalizeChildren(vnode, children)
|
|
|
|
|
2019-09-01 02:17:46 +00:00
|
|
|
// presence of a patch flag indicates this node needs patching on updates.
|
|
|
|
// component nodes also should always be patched, because even if the
|
2019-05-31 18:14:49 +00:00
|
|
|
// 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.
|
2019-06-02 08:35:19 +00:00
|
|
|
if (
|
2019-10-23 21:57:40 +00:00
|
|
|
shouldTrack > 0 &&
|
2020-04-24 16:18:51 +00:00
|
|
|
!isBlockNode &&
|
2020-03-18 22:14:51 +00:00
|
|
|
currentBlock &&
|
2020-02-13 22:47:00 +00:00
|
|
|
// the EVENTS flag is only for hydration and if it is the only flag, the
|
2020-02-13 23:28:40 +00:00
|
|
|
// vnode should not be considered dynamic due to handler caching.
|
2020-02-13 22:47:00 +00:00
|
|
|
patchFlag !== PatchFlags.HYDRATE_EVENTS &&
|
2019-10-21 15:30:45 +00:00
|
|
|
(patchFlag > 0 ||
|
2020-01-22 15:45:27 +00:00
|
|
|
shapeFlag & ShapeFlags.SUSPENSE ||
|
2020-05-01 19:12:07 +00:00
|
|
|
shapeFlag & ShapeFlags.TELEPORT ||
|
2019-08-22 15:12:37 +00:00
|
|
|
shapeFlag & ShapeFlags.STATEFUL_COMPONENT ||
|
|
|
|
shapeFlag & ShapeFlags.FUNCTIONAL_COMPONENT)
|
2019-06-02 08:35:19 +00:00
|
|
|
) {
|
2019-10-21 15:30:45 +00:00
|
|
|
currentBlock.push(vnode)
|
2018-09-24 23:11:14 +00:00
|
|
|
}
|
2019-06-01 09:43:41 +00:00
|
|
|
|
2019-05-25 15:51:20 +00:00
|
|
|
return vnode
|
|
|
|
}
|
2018-09-19 15:35:38 +00:00
|
|
|
|
2019-10-28 00:54:54 +00:00
|
|
|
export function cloneVNode<T, U>(
|
|
|
|
vnode: VNode<T, U>,
|
2019-11-20 23:04:44 +00:00
|
|
|
extraProps?: Data & VNodeProps
|
2019-10-28 00:54:54 +00:00
|
|
|
): VNode<T, U> {
|
2020-04-24 16:42:46 +00:00
|
|
|
const props = (extraProps
|
|
|
|
? vnode.props
|
|
|
|
? mergeProps(vnode.props, extraProps)
|
|
|
|
: extend({}, extraProps)
|
|
|
|
: vnode.props) as any
|
2019-10-28 00:45:54 +00:00
|
|
|
// This is intentionally NOT using spread or extend to avoid the runtime
|
|
|
|
// key enumeration cost.
|
2019-08-22 21:12:39 +00:00
|
|
|
return {
|
2019-09-30 14:45:50 +00:00
|
|
|
_isVNode: true,
|
2019-08-22 21:12:39 +00:00
|
|
|
type: vnode.type,
|
2020-04-24 16:42:46 +00:00
|
|
|
props,
|
2020-04-24 16:56:38 +00:00
|
|
|
key: props && normalizeKey(props),
|
|
|
|
ref: props && normalizeRef(props),
|
2019-12-16 18:33:10 +00:00
|
|
|
scopeId: vnode.scopeId,
|
2019-09-02 16:09:29 +00:00
|
|
|
children: vnode.children,
|
|
|
|
target: vnode.target,
|
2020-03-28 03:12:52 +00:00
|
|
|
targetAnchor: vnode.targetAnchor,
|
2019-08-22 21:12:39 +00:00
|
|
|
shapeFlag: vnode.shapeFlag,
|
2020-05-01 18:55:27 +00:00
|
|
|
// if the vnode is cloned with extra props, we can no longer assume its
|
|
|
|
// existing patch flag to be reliable and need to bail out of optimized mode.
|
|
|
|
// however we don't want block nodes to de-opt their children, so if the
|
|
|
|
// vnode is a block node, we only add the FULL_PROPS flag to it.
|
|
|
|
patchFlag: extraProps
|
|
|
|
? vnode.dynamicChildren
|
|
|
|
? vnode.patchFlag | PatchFlags.FULL_PROPS
|
|
|
|
: PatchFlags.BAIL
|
|
|
|
: vnode.patchFlag,
|
2019-08-22 21:12:39 +00:00
|
|
|
dynamicProps: vnode.dynamicProps,
|
2019-09-02 16:09:29 +00:00
|
|
|
dynamicChildren: vnode.dynamicChildren,
|
2019-09-02 20:09:34 +00:00
|
|
|
appContext: vnode.appContext,
|
2019-10-26 20:00:07 +00:00
|
|
|
dirs: vnode.dirs,
|
2019-11-20 23:04:44 +00:00
|
|
|
transition: vnode.transition,
|
2019-09-02 16:09:29 +00:00
|
|
|
|
2019-10-30 02:28:38 +00:00
|
|
|
// 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,
|
|
|
|
el: vnode.el,
|
|
|
|
anchor: vnode.anchor
|
2019-08-22 21:12:39 +00:00
|
|
|
}
|
2019-05-25 15:51:20 +00:00
|
|
|
}
|
2019-05-28 05:27:31 +00:00
|
|
|
|
2020-04-30 21:04:35 +00:00
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
2019-10-24 21:55:00 +00:00
|
|
|
export function createTextVNode(text: string = ' ', flag: number = 0): VNode {
|
|
|
|
return createVNode(Text, null, text, flag)
|
|
|
|
}
|
|
|
|
|
2020-04-30 21:04:35 +00:00
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
2020-02-12 16:56:42 +00:00
|
|
|
export function createStaticVNode(content: string): VNode {
|
|
|
|
return createVNode(Static, null, content)
|
|
|
|
}
|
|
|
|
|
2020-04-30 21:04:35 +00:00
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
2019-10-25 01:19:02 +00:00
|
|
|
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
|
2020-02-11 23:12:56 +00:00
|
|
|
? (openBlock(), createBlock(Comment, null, text))
|
2019-10-25 01:19:02 +00:00
|
|
|
: createVNode(Comment, null, text)
|
2019-10-24 21:55:00 +00:00
|
|
|
}
|
|
|
|
|
2020-03-23 15:08:22 +00:00
|
|
|
export function normalizeVNode(child: VNodeChild): VNode {
|
2020-01-06 16:57:19 +00:00
|
|
|
if (child == null || typeof child === 'boolean') {
|
2019-05-28 05:27:31 +00:00
|
|
|
// empty placeholder
|
2019-09-24 18:37:14 +00:00
|
|
|
return createVNode(Comment)
|
2019-05-28 05:27:31 +00:00
|
|
|
} else if (isArray(child)) {
|
|
|
|
// fragment
|
|
|
|
return createVNode(Fragment, null, child)
|
|
|
|
} else if (typeof child === 'object') {
|
2019-05-31 10:07:43 +00:00
|
|
|
// already vnode, this should be the most common since compiled templates
|
|
|
|
// always produce all-vnode children arrays
|
2019-08-22 21:12:39 +00:00
|
|
|
return child.el === null ? child : cloneVNode(child)
|
2019-05-28 05:27:31 +00:00
|
|
|
} else {
|
2020-01-06 16:57:19 +00:00
|
|
|
// strings and numbers
|
2019-12-04 17:28:52 +00:00
|
|
|
return createVNode(Text, null, String(child))
|
2019-05-28 05:27:31 +00:00
|
|
|
}
|
|
|
|
}
|
2019-05-31 10:07:43 +00:00
|
|
|
|
2019-12-22 18:31:13 +00:00
|
|
|
// optimized normalization for template-compiled render fns
|
|
|
|
export function cloneIfMounted(child: VNode): VNode {
|
2019-12-22 21:24:24 +00:00
|
|
|
return child.el === null ? child : cloneVNode(child)
|
2019-12-22 18:31:13 +00:00
|
|
|
}
|
|
|
|
|
2019-06-02 14:22:44 +00:00
|
|
|
export function normalizeChildren(vnode: VNode, children: unknown) {
|
|
|
|
let type = 0
|
2020-03-27 22:42:57 +00:00
|
|
|
const { shapeFlag } = vnode
|
2019-05-31 10:07:43 +00:00
|
|
|
if (children == null) {
|
2019-06-02 14:22:44 +00:00
|
|
|
children = null
|
2019-05-31 10:07:43 +00:00
|
|
|
} else if (isArray(children)) {
|
2019-08-22 15:12:37 +00:00
|
|
|
type = ShapeFlags.ARRAY_CHILDREN
|
2019-05-31 10:07:43 +00:00
|
|
|
} else if (typeof children === 'object') {
|
2020-03-27 22:42:57 +00:00
|
|
|
// Normalize slot to plain children
|
|
|
|
if (
|
2020-03-31 14:52:42 +00:00
|
|
|
(shapeFlag & ShapeFlags.ELEMENT || shapeFlag & ShapeFlags.TELEPORT) &&
|
2020-03-27 22:42:57 +00:00
|
|
|
(children as any).default
|
|
|
|
) {
|
2020-03-23 18:47:04 +00:00
|
|
|
normalizeChildren(vnode, (children as any).default())
|
|
|
|
return
|
|
|
|
} else {
|
|
|
|
type = ShapeFlags.SLOTS_CHILDREN
|
2020-04-15 20:45:20 +00:00
|
|
|
if (!(children as RawSlots)._ && !(InternalObjectKey in children!)) {
|
2020-04-07 01:06:48 +00:00
|
|
|
// if slots are not normalized, attach context instance
|
|
|
|
// (compiled / normalized slots already have context)
|
2020-03-23 18:47:04 +00:00
|
|
|
;(children as RawSlots)._ctx = currentRenderingInstance
|
|
|
|
}
|
2020-03-16 16:20:52 +00:00
|
|
|
}
|
2019-05-31 10:07:43 +00:00
|
|
|
} else if (isFunction(children)) {
|
2020-03-16 16:20:52 +00:00
|
|
|
children = { default: children, _ctx: currentRenderingInstance }
|
2019-08-22 15:12:37 +00:00
|
|
|
type = ShapeFlags.SLOTS_CHILDREN
|
2019-05-31 10:07:43 +00:00
|
|
|
} else {
|
2019-12-04 17:28:52 +00:00
|
|
|
children = String(children)
|
2020-03-31 14:52:42 +00:00
|
|
|
// force teleport children to array so it can be moved around
|
|
|
|
if (shapeFlag & ShapeFlags.TELEPORT) {
|
2020-03-27 22:42:57 +00:00
|
|
|
type = ShapeFlags.ARRAY_CHILDREN
|
|
|
|
children = [createTextVNode(children as string)]
|
|
|
|
} else {
|
|
|
|
type = ShapeFlags.TEXT_CHILDREN
|
|
|
|
}
|
2019-05-31 10:07:43 +00:00
|
|
|
}
|
2020-01-29 03:58:02 +00:00
|
|
|
vnode.children = children as VNodeNormalizedChildren
|
2019-06-02 14:22:44 +00:00
|
|
|
vnode.shapeFlag |= type
|
2019-05-31 10:07:43 +00:00
|
|
|
}
|
2019-06-01 09:43:41 +00:00
|
|
|
|
2019-08-22 21:12:39 +00:00
|
|
|
const handlersRE = /^on|^vnode/
|
|
|
|
|
2019-11-20 23:04:44 +00:00
|
|
|
export function mergeProps(...args: (Data & VNodeProps)[]) {
|
2019-08-22 21:12:39 +00:00
|
|
|
const ret: Data = {}
|
2019-08-23 02:07:51 +00:00
|
|
|
extend(ret, args[0])
|
2019-08-22 21:12:39 +00:00
|
|
|
for (let i = 1; i < args.length; i++) {
|
|
|
|
const toMerge = args[i]
|
|
|
|
for (const key in toMerge) {
|
|
|
|
if (key === 'class') {
|
2020-02-28 22:53:26 +00:00
|
|
|
if (ret.class !== toMerge.class) {
|
|
|
|
ret.class = normalizeClass([ret.class, toMerge.class])
|
|
|
|
}
|
2019-08-22 21:12:39 +00:00
|
|
|
} else if (key === 'style') {
|
|
|
|
ret.style = normalizeStyle([ret.style, toMerge.style])
|
|
|
|
} else if (handlersRE.test(key)) {
|
|
|
|
// on*, vnode*
|
|
|
|
const existing = ret[key]
|
2020-02-28 22:53:26 +00:00
|
|
|
const incoming = toMerge[key]
|
|
|
|
if (existing !== incoming) {
|
|
|
|
ret[key] = existing
|
|
|
|
? [].concat(existing as any, toMerge[key] as any)
|
|
|
|
: incoming
|
|
|
|
}
|
2019-08-22 21:12:39 +00:00
|
|
|
} else {
|
|
|
|
ret[key] = toMerge[key]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret
|
|
|
|
}
|