import { EMPTY_OBJ, isFunction, capitalize, invokeHandlers } from '@vue/shared'
import { RenderProxyHandlers } from './componentProxy'
import { ComponentPropsOptions, ExtractPropTypes } from './componentProps'
-import { PROPS, DYNAMIC_SLOTS, FULL_PROPS } from './patchFlags'
import { Slots } from './componentSlots'
-import { STATEFUL_COMPONENT } from './typeFlags'
+import { PatchFlags } from './patchFlags'
+import { ShapeFlags } from './shapeFlags'
export type Data = { [key: string]: unknown }
parent,
root
} = instance
- if (vnode.shapeFlag & STATEFUL_COMPONENT) {
+ if (vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT) {
return normalizeVNode(
(instance.render as RenderFunction).call(renderProxy, props, setupContext)
)
const { props: prevProps, children: prevChildren } = prevVNode
const { props: nextProps, children: nextChildren, patchFlag } = nextVNode
if (patchFlag) {
- if (patchFlag & DYNAMIC_SLOTS) {
+ if (patchFlag & PatchFlags.DYNAMIC_SLOTS) {
// slot content that references values that might have changed,
// e.g. in a v-for
return true
}
- if (patchFlag & FULL_PROPS) {
+ if (patchFlag & PatchFlags.FULL_PROPS) {
// presence of this flag indicates props are always non-null
return hasPropsChanged(prevProps as Data, nextProps as Data)
- } else if (patchFlag & PROPS) {
+ } else if (patchFlag & PatchFlags.PROPS) {
const dynamicProps = nextVNode.dynamicProps as string[]
for (let i = 0; i < dynamicProps.length; i++) {
const key = dynamicProps[i]
} from '@vue/shared'
import { warn } from './warning'
import { Data, ComponentInstance } from './component'
-import { FULL_PROPS } from './patchFlags'
+import { PatchFlags } from './patchFlags'
export type ComponentPropsOptions<P = Data> = {
[K in keyof P]: Prop<P[K]> | null
// in case of dynamic props, check if we need to delete keys from
// the props proxy
const { patchFlag } = instance.vnode
- if (propsProxy !== null && (patchFlag === 0 || patchFlag & FULL_PROPS)) {
+ if (
+ propsProxy !== null &&
+ (patchFlag === 0 || patchFlag & PatchFlags.FULL_PROPS)
+ ) {
const rawInitialProps = toRaw(propsProxy)
for (const key in rawInitialProps) {
if (!props.hasOwnProperty(key)) {
import { ComponentInstance } from './component'
import { VNode, NormalizedChildren, normalizeVNode, VNodeChild } from './vnode'
import { isArray, isFunction } from '@vue/shared'
-import { SLOTS_CHILDREN } from './typeFlags'
+import { ShapeFlags } from './shapeFlags'
export type Slot = (...args: any[]) => VNode[]
export type Slots = Readonly<{
children: NormalizedChildren
) {
let slots: Slots | void
- if (instance.vnode.shapeFlag & SLOTS_CHILDREN) {
+ if (instance.vnode.shapeFlag & ShapeFlags.SLOTS_CHILDREN) {
if ((children as any)._normalized) {
// pre-normalized slots object generated by compiler
slots = children as Slots
isReservedProp,
isFunction
} from '@vue/shared'
-import {
- TEXT,
- CLASS,
- STYLE,
- PROPS,
- KEYED,
- UNKEYED,
- FULL_PROPS
-} from './patchFlags'
import { queueJob, queuePostFlushCb, flushPostFlushCbs } from './scheduler'
import {
effect,
} from '@vue/reactivity'
import { resolveProps } from './componentProps'
import { resolveSlots } from './componentSlots'
-import {
- ELEMENT,
- STATEFUL_COMPONENT,
- FUNCTIONAL_COMPONENT,
- TEXT_CHILDREN,
- ARRAY_CHILDREN
-} from './typeFlags'
+import { PatchFlags } from './patchFlags'
+import { ShapeFlags } from './shapeFlags'
const prodEffectOptions = {
scheduler: queueJob
)
break
default:
- if (shapeFlag & ELEMENT) {
+ if (shapeFlag & ShapeFlags.ELEMENT) {
processElement(
n1,
n2,
} else {
if (
__DEV__ &&
- !(shapeFlag & STATEFUL_COMPONENT) &&
- !(shapeFlag & FUNCTIONAL_COMPONENT)
+ !(shapeFlag & ShapeFlags.STATEFUL_COMPONENT) &&
+ !(shapeFlag & ShapeFlags.FUNCTIONAL_COMPONENT)
) {
// TODO warn invalid node type
debugger
hostPatchProp(el, key, props[key], null, isSVG)
}
}
- if (shapeFlag & TEXT_CHILDREN) {
+ if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
hostSetElementText(el, vnode.children as string)
- } else if (shapeFlag & ARRAY_CHILDREN) {
+ } else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
mountChildren(
vnode.children as VNodeChildren,
el,
// in this path old node and new node are guaranteed to have the same shape
// (i.e. at the exact same position in the source template)
- if (patchFlag & FULL_PROPS) {
+ if (patchFlag & PatchFlags.FULL_PROPS) {
// element props contain dynamic keys, full diff needed
patchProps(el, n2, oldProps, newProps, parentComponent, isSVG)
} else {
// class
// this flag is matched when the element has dynamic class bindings.
- if (patchFlag & CLASS) {
+ if (patchFlag & PatchFlags.CLASS) {
if (oldProps.class !== newProps.class) {
hostPatchProp(el, 'class', newProps.class, null, isSVG)
}
// style
// this flag is matched when the element has dynamic style bindings
- if (patchFlag & STYLE) {
+ if (patchFlag & PatchFlags.STYLE) {
hostPatchProp(el, 'style', newProps.style, oldProps.style, isSVG)
}
// faster iteration.
// Note dynamic keys like :[foo]="bar" will cause this optimization to
// bail out and go through a full diff because we need to unset the old key
- if (patchFlag & PROPS) {
+ if (patchFlag & PatchFlags.PROPS) {
// if the flag is present then dynamicProps must be non-null
const propsToUpdate = n2.dynamicProps as string[]
for (let i = 0; i < propsToUpdate.length; i++) {
// text
// This flag is matched when the element has only dynamic text children.
// this flag is terminal (i.e. skips children diffing).
- if (patchFlag & TEXT) {
+ if (patchFlag & PatchFlags.TEXT) {
if (n1.children !== n2.children) {
hostSetElementText(el, n2.children as string)
}
? hostQuerySelector(targetSelector)
: null)
if (target != null) {
- if (shapeFlag & TEXT_CHILDREN) {
+ if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
hostSetElementText(target, children as string)
- } else if (shapeFlag & ARRAY_CHILDREN) {
+ } else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
mountChildren(
children as VNodeChildren,
target,
} else {
// update content
const target = (n2.target = n1.target)
- if (patchFlag === TEXT) {
+ if (patchFlag === PatchFlags.TEXT) {
hostSetElementText(target, children as string)
} else if (!optimized) {
patchChildren(n1, n2, target, null, parentComponent, isSVG)
: null)
if (nextTarget != null) {
// move content
- if (shapeFlag & TEXT_CHILDREN) {
+ if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
hostSetElementText(target, '')
hostSetElementText(nextTarget, children as string)
- } else if (shapeFlag & ARRAY_CHILDREN) {
+ } else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
for (let i = 0; i < (children as VNode[]).length; i++) {
move((children as VNode[])[i], nextTarget, null)
}
resolveProps(instance, initialVNode.props, Component.props)
resolveSlots(instance, initialVNode.children)
// setup stateful
- if (initialVNode.shapeFlag & STATEFUL_COMPONENT) {
+ if (initialVNode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT) {
setupStatefulComponent(instance)
}
const subTree = (instance.subTree = renderComponentRoot(instance))
// fast path
const { patchFlag, shapeFlag } = n2
if (patchFlag) {
- if (patchFlag & KEYED) {
+ if (patchFlag & PatchFlags.KEYED) {
// this could be either fully-keyed or mixed (some keyed some not)
// presence of patchFlag means children are guaranteed to be arrays
patchKeyedChildren(
optimized
)
return
- } else if (patchFlag & UNKEYED) {
+ } else if (patchFlag & PatchFlags.UNKEYED) {
// unkeyed
patchUnkeyedChildren(
c1 as VNode[],
}
}
- if (shapeFlag & TEXT_CHILDREN) {
+ if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
// text children fast path
- if (prevShapeFlag & ARRAY_CHILDREN) {
+ if (prevShapeFlag & ShapeFlags.ARRAY_CHILDREN) {
unmountChildren(c1 as VNode[], parentComponent)
}
hostSetElementText(container, c2 as string)
} else {
- if (prevShapeFlag & TEXT_CHILDREN) {
+ if (prevShapeFlag & ShapeFlags.TEXT_CHILDREN) {
hostSetElementText(container, '')
- if (shapeFlag & ARRAY_CHILDREN) {
+ if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
mountChildren(
c2 as VNodeChildren,
container,
isSVG
)
}
- } else if (prevShapeFlag & ARRAY_CHILDREN) {
- if (shapeFlag & ARRAY_CHILDREN) {
+ } else if (prevShapeFlag & ShapeFlags.ARRAY_CHILDREN) {
+ if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
// two arrays, cannot assume anything, do full diff
patchKeyedChildren(
c1 as VNode[],
parentComponent,
shouldRemoveChildren
)
- } else if (vnode.shapeFlag & ARRAY_CHILDREN) {
+ } else if (vnode.shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
unmountChildren(
vnode.children as VNode[],
parentComponent,
+// Types
+export { VNode } from './vnode'
+export { FunctionalComponent } from './component'
+export { RendererOptions } from './createRenderer'
+export { Slot, Slots } from './componentSlots'
+export { PropType, ComponentPropsOptions } from './componentProps'
+
+// API
export {
- VNode,
openBlock,
createBlock,
createVNode,
Fragment,
Portal
} from './vnode'
-
+export { createComponent, getCurrentInstance } from './component'
+export { createRenderer } from './createRenderer'
export { nextTick } from './scheduler'
-export {
- createComponent,
- getCurrentInstance,
- FunctionalComponent
-} from './component'
-export { createRenderer, RendererOptions } from './createRenderer'
-export { Slot, Slots } from './componentSlots'
-export { PropType, ComponentPropsOptions } from './componentProps'
-
export * from './apiReactivity'
export * from './apiWatch'
export * from './apiLifecycle'
export * from './apiInject'
-export * from './patchFlags'
-export * from './typeFlags'
+
+// Flags
+export { PublicPatchFlags as PatchFlags } from './patchFlags'
+export { PublicShapeFlags as ShapeFlags } from './shapeFlags'
// Check the `patchElement` function in './createRednerer.ts' to see how the
// flags are handled during diff.
-// Indicates an element with dynamic textContent (children fast path)
-export const TEXT = 1
-
-// Indicates an element with dynamic class.
-// The compiler also pre-normalizes the :class binding:
-// - b -> normalize(b)
-// - ['foo', b] -> 'foo' + normalize(b)
-// - { a, b: c } -> (a ? a : '') + (b ? c : '')
-// - ['a', b, { c }] -> 'a' + normalize(b) + (c ? c : '')
-export const CLASS = 1 << 1
-
-// Indicates an element with dynamic style
-// The compiler pre-compiles static string styles into static objects
-// + detects and hoists inline static objects
-// e.g. style="color: red" and :style="{ color: 'red' }" both get hoisted as
-// const style = { color: 'red' }
-// render() { return e('div', { style }) }
-export const STYLE = 1 << 2
-
-// Indicates an element that has non-class/style dynamic props.
-// Can also be on a component that has any dynamic props (includes class/style).
-// when this flag is present, the vnode also has a dynamicProps array that
-// contains the keys of the props that may change so the runtime can diff
-// them faster (without having to worry about removed props)
-export const PROPS = 1 << 3
-
-// Indicates an element with props with dynamic keys. When keys change, a full
-// diff is always needed to remove the old key. This flag is mutually exclusive
-// with CLASS, STYLE and PROPS.
-export const FULL_PROPS = 1 << 4
-
-// Indicates a fragment or element with keyed or partially-keyed v-for children
-export const KEYED = 1 << 5
-
-// Indicates a fragment or element that contains unkeyed v-for children
-export const UNKEYED = 1 << 6
-
-// Indicates a component with dynamic slots (e.g. slot that references a v-for
-// iterated value, or dynamic slot names).
-// Components with this flag are always force updated.
-export const DYNAMIC_SLOTS = 1 << 7
-
-// Indicates an element with ref. This includes static string refs because the
-// refs object is refreshed on each update and all refs need to set again.
-export const REF = 1 << 8
+export const enum PatchFlags {
+ // Indicates an element with dynamic textContent (children fast path)
+ TEXT = 1,
+
+ // Indicates an element with dynamic class.
+ // The compiler also pre-normalizes the :class binding:
+ // - b -> normalize(b)
+ // - ['foo', b] -> 'foo' + normalize(b)
+ // - { a, b: c } -> (a ? a : '') + (b ? c : '')
+ // - ['a', b, { c }] -> 'a' + normalize(b) + (c ? c : '')
+ CLASS = 1 << 1,
+
+ // Indicates an element with dynamic style
+ // The compiler pre-compiles static string styles into static objects
+ // + detects and hoists inline static objects
+ // e.g. style="color: red" and :style="{ color: 'red' }" both get hoisted as
+ // const style = { color: 'red' }
+ // render() { return e('div', { style }) }
+ STYLE = 1 << 2,
+
+ // Indicates an element that has non-class/style dynamic props.
+ // Can also be on a component that has any dynamic props (includes
+ // class/style). when this flag is present, the vnode also has a dynamicProps
+ // array that contains the keys of the props that may change so the runtime
+ // can diff them faster (without having to worry about removed props)
+ PROPS = 1 << 3,
+
+ // Indicates an element with props with dynamic keys. When keys change, a full
+ // diff is always needed to remove the old key. This flag is mutually
+ // exclusive with CLASS, STYLE and PROPS.
+ FULL_PROPS = 1 << 4,
+
+ // Indicates a fragment or element with keyed or partially-keyed v-for
+ // children
+ KEYED = 1 << 5,
+
+ // Indicates a fragment or element that contains unkeyed v-for children
+ UNKEYED = 1 << 6,
+
+ // Indicates a component with dynamic slots (e.g. slot that references a v-for
+ // iterated value, or dynamic slot names).
+ // Components with this flag are always force updated.
+ DYNAMIC_SLOTS = 1 << 7,
+
+ // Indicates an element with ref. This includes static string refs because the
+ // refs object is refreshed on each update and all refs need to set again.
+ REF = 1 << 8
+}
+
+// runtime object for public consumption
+export const PublicPatchFlags = {
+ TEXT: PatchFlags.TEXT,
+ CLASS: PatchFlags.CLASS,
+ STYLE: PatchFlags.STYLE,
+ PROPS: PatchFlags.PROPS,
+ FULL_PROPS: PatchFlags.FULL_PROPS,
+ KEYED: PatchFlags.KEYED,
+ UNKEYED: PatchFlags.UNKEYED,
+ REF: PatchFlags.REF
+}
--- /dev/null
+// internally the const enum flags are used to avoid overhead of property
+// access
+export const enum ShapeFlags {
+ ELEMENT = 1,
+ FUNCTIONAL_COMPONENT = 1 << 1,
+ STATEFUL_COMPONENT = 1 << 2,
+ TEXT_CHILDREN = 1 << 3,
+ ARRAY_CHILDREN = 1 << 4,
+ SLOTS_CHILDREN = 1 << 5
+}
+
+// but the flags are also exported as an actual object for external use
+export const PublicShapeFlags = {
+ ELEMENT: ShapeFlags.ELEMENT,
+ FUNCTIONAL_COMPONENT: ShapeFlags.FUNCTIONAL_COMPONENT,
+ STATEFUL_COMPONENT: ShapeFlags.STATEFUL_COMPONENT,
+ TEXT_CHILDREN: ShapeFlags.TEXT_CHILDREN,
+ ARRAY_CHILDREN: ShapeFlags.ARRAY_CHILDREN,
+ SLOTS_CHILDREN: ShapeFlags.SLOTS_CHILDREN
+}
+++ /dev/null
-export const ELEMENT = 1
-export const FUNCTIONAL_COMPONENT = 1 << 1
-export const STATEFUL_COMPONENT = 1 << 2
-export const TEXT_CHILDREN = 1 << 3
-export const ARRAY_CHILDREN = 1 << 4
-export const SLOTS_CHILDREN = 1 << 5
import { isArray, isFunction, isString, isObject, EMPTY_ARR } from '@vue/shared'
-import { ComponentInstance } from './component'
+import { ComponentInstance, Data } from './component'
import { HostNode } from './createRenderer'
import { RawSlots } from './componentSlots'
-import { CLASS } from './patchFlags'
-import {
- ELEMENT,
- FUNCTIONAL_COMPONENT,
- STATEFUL_COMPONENT,
- TEXT_CHILDREN,
- ARRAY_CHILDREN,
- SLOTS_CHILDREN
-} from './typeFlags'
+import { PatchFlags } from './patchFlags'
+import { ShapeFlags } from './shapeFlags'
export const Fragment = Symbol('Fragment')
export const Text = Symbol('Text')
props = props || null
// encode the vnode type information into a bitmap
- const typeFlag = isString(type)
- ? ELEMENT
+ const shapeFlag = isString(type)
+ ? ShapeFlags.ELEMENT
: isObject(type)
- ? STATEFUL_COMPONENT
+ ? ShapeFlags.STATEFUL_COMPONENT
: isFunction(type)
- ? FUNCTIONAL_COMPONENT
+ ? ShapeFlags.FUNCTIONAL_COMPONENT
: 0
const vnode: VNode = {
el: null,
anchor: null,
target: null,
- shapeFlag: typeFlag,
+ shapeFlag,
patchFlag,
dynamicProps,
dynamicChildren: null
if (props !== null) {
// class normalization only needed if the vnode isn't generated by
// compiler-optimized code
- if (props.class != null && !(patchFlag & CLASS)) {
+ if (props.class != null && !(patchFlag & PatchFlags.CLASS)) {
props.class = normalizeClass(props.class)
}
if (props.style != null) {
if (
shouldTrack &&
(patchFlag ||
- typeFlag & STATEFUL_COMPONENT ||
- typeFlag & FUNCTIONAL_COMPONENT)
+ shapeFlag & ShapeFlags.STATEFUL_COMPONENT ||
+ shapeFlag & ShapeFlags.FUNCTIONAL_COMPONENT)
) {
trackDynamicNode(vnode)
}
}
}
-export function cloneVNode(vnode: VNode): VNode {
+export function cloneVNode(vnode: VNode, extraProps?: Data): VNode {
// TODO
return vnode
}
if (children == null) {
children = null
} else if (isArray(children)) {
- type = ARRAY_CHILDREN
+ type = ShapeFlags.ARRAY_CHILDREN
} else if (typeof children === 'object') {
- type = SLOTS_CHILDREN
+ type = ShapeFlags.SLOTS_CHILDREN
} else if (isFunction(children)) {
children = { default: children }
- type = SLOTS_CHILDREN
+ type = ShapeFlags.SLOTS_CHILDREN
} else {
children = isString(children) ? children : children + ''
- type = TEXT_CHILDREN
+ type = ShapeFlags.TEXT_CHILDREN
}
vnode.children = children as NormalizedChildren
vnode.shapeFlag |= type