| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449 |
- import {
- ComponentInstance,
- ComponentClass,
- FunctionalComponent
- } from './component'
- import { VNodeFlags, ChildrenFlags } from './flags'
- import { createComponentClassFromOptions } from './componentOptions'
- import { EMPTY_OBJ, isObject, isArray, isFunction, isString } from '@vue/shared'
- import { RawChildrenType, RawSlots } from './h'
- import { FunctionalHandle } from './createRenderer'
- const handlersRE = /^on|^vnode/
- const STABLE_SLOTS_HINT = '$stable'
- // Vue core is platform agnostic, so we are not using Element for "DOM" nodes.
- export interface RenderNode {
- vnode?: VNode | null
- // technically this doesn't exist on platform render nodes,
- // but we list it here so that TS can figure out union types
- $f: false
- }
- export interface VNode {
- _isVNode: true
- flags: VNodeFlags
- tag: string | FunctionalComponent | ComponentClass | RenderNode | null
- data: VNodeData | null
- children: VNodeChildren
- childFlags: ChildrenFlags
- key: Key | null
- ref: Ref | null
- slots: Slots | null
- // only on mounted nodes
- el: RenderNode | null
- // only on mounted component nodes that is also a root node (HOCs)
- // points to parent component's placeholder vnode
- // this is used to update vnode.el for nested HOCs.
- parentVNode: VNode | null
- // only on mounted component nodes
- // points to the parent stateful/functional component's placeholder node
- contextVNode: VNode | null
- // only on mounted functional component nodes
- // a consistent handle so that a functional component can be identified
- // by the scheduler
- handle: FunctionalHandle | null
- // only on cloned vnodes, points to the original cloned vnode
- clonedFrom: VNode | null
- }
- export interface MountedVNode extends VNode {
- el: RenderNode
- }
- export interface BuiltInProps {
- key?: Key | null
- ref?: Ref | null
- slots?: RawSlots | null
- }
- export type VNodeData = {
- [key: string]: any
- } & BuiltInProps
- export type VNodeChildren =
- | VNode[] // ELEMENT | PORTAL
- | ComponentInstance // COMPONENT_STATEFUL
- | VNode // COMPONENT_FUNCTIONAL
- | string // TEXT
- | null
- export type Key = string | number
- export type Ref = (t: RenderNode | ComponentInstance | null) => void
- export type Slot = (...args: any[]) => VNode[]
- export type Slots = Readonly<{
- [name: string]: Slot
- }>
- export function createVNode(
- flags: VNodeFlags,
- tag: string | FunctionalComponent | ComponentClass | RenderNode | null,
- data: VNodeData | null,
- children: RawChildrenType | null,
- childFlags: ChildrenFlags,
- key: Key | null | undefined,
- ref: Ref | null | undefined,
- slots: Slots | null | undefined
- ): VNode {
- const vnode: VNode = {
- _isVNode: true,
- flags,
- tag,
- data,
- children: children as VNodeChildren,
- childFlags,
- key: key === void 0 ? null : key,
- ref: ref === void 0 ? null : ref,
- slots: slots === void 0 ? null : slots,
- el: null,
- parentVNode: null,
- contextVNode: null,
- handle: null,
- clonedFrom: null
- }
- if (childFlags === ChildrenFlags.UNKNOWN_CHILDREN) {
- normalizeChildren(vnode, children)
- }
- return vnode
- }
- export function createElementVNode(
- tag: string,
- data: VNodeData | null,
- children: RawChildrenType | null,
- childFlags: ChildrenFlags,
- key?: Key | null,
- ref?: Ref | null
- ) {
- const flags = tag === 'svg' ? VNodeFlags.ELEMENT_SVG : VNodeFlags.ELEMENT_HTML
- if (data !== null) {
- normalizeClassAndStyle(data)
- }
- return createVNode(flags, tag, data, children, childFlags, key, ref, null)
- }
- function normalizeClassAndStyle(data: VNodeData) {
- if (data.class != null) {
- data.class = normalizeClass(data.class)
- }
- if (data.style != null) {
- data.style = normalizeStyle(data.style)
- }
- }
- function normalizeStyle(value: any): Record<string, string | number> | void {
- if (isArray(value)) {
- const res: Record<string, string | number> = {}
- for (let i = 0; i < value.length; i++) {
- const normalized = normalizeStyle(value[i])
- if (normalized) {
- for (const key in normalized) {
- res[key] = normalized[key]
- }
- }
- }
- return res
- } else if (isObject(value)) {
- return value
- }
- }
- function normalizeClass(value: any): string {
- let res = ''
- if (isString(value)) {
- res = value
- } else if (isArray(value)) {
- for (let i = 0; i < value.length; i++) {
- res += normalizeClass(value[i]) + ' '
- }
- } else if (isObject(value)) {
- for (const name in value) {
- if (value[name]) {
- res += name + ' '
- }
- }
- }
- return res.trim()
- }
- export function createComponentVNode(
- comp: any,
- data: VNodeData | null,
- children: RawChildrenType | Slots,
- childFlags: ChildrenFlags,
- key?: Key | null,
- ref?: Ref | null
- ) {
- // resolve type
- let flags: VNodeFlags
- // flags
- if (isObject(comp)) {
- if (comp.functional) {
- // object literal functional
- flags = VNodeFlags.COMPONENT_FUNCTIONAL
- const { render } = comp
- if (!comp._normalized) {
- render.pure = comp.pure
- render.props = comp.props
- comp._normalized = true
- }
- comp = render
- } else {
- // object literal stateful
- flags = VNodeFlags.COMPONENT_STATEFUL_NORMAL
- comp =
- comp._normalized ||
- (comp._normalized = createComponentClassFromOptions(comp))
- }
- } else {
- // assumes comp is function here now
- if (__DEV__ && !isFunction(comp)) {
- // TODO warn invalid comp value in dev
- }
- if (comp.prototype && comp.prototype.render) {
- flags = VNodeFlags.COMPONENT_STATEFUL_NORMAL
- } else {
- flags = VNodeFlags.COMPONENT_FUNCTIONAL
- }
- }
- if (__DEV__ && flags === VNodeFlags.COMPONENT_FUNCTIONAL && ref) {
- // TODO warn functional component cannot have ref
- }
- // slots
- let slots: any
- if (childFlags === ChildrenFlags.STABLE_SLOTS) {
- slots = children
- } else if (childFlags === ChildrenFlags.UNKNOWN_CHILDREN) {
- childFlags = children
- ? ChildrenFlags.DYNAMIC_SLOTS
- : ChildrenFlags.NO_CHILDREN
- if (children != null) {
- if (isFunction(children)) {
- // function as children
- slots = { default: children }
- } else if (isObject(children) && !(children as any)._isVNode) {
- // slot object as children
- slots = children
- // special manual optimization hint for raw render fn users
- if (slots[STABLE_SLOTS_HINT]) {
- childFlags = ChildrenFlags.STABLE_SLOTS
- }
- } else {
- slots = { default: () => children }
- }
- slots = normalizeSlots(slots)
- }
- }
- // class & style
- if (data !== null) {
- normalizeClassAndStyle(data)
- }
- return createVNode(
- flags,
- comp,
- data,
- null, // to be set during mount
- childFlags,
- key,
- ref,
- slots
- )
- }
- export function createTextVNode(text: string): VNode {
- return createVNode(
- VNodeFlags.TEXT,
- null,
- null,
- text == null ? '' : text,
- ChildrenFlags.NO_CHILDREN,
- null,
- null,
- null
- )
- }
- export function createFragment(
- children: RawChildrenType,
- childFlags?: ChildrenFlags,
- key?: Key | null
- ) {
- return createVNode(
- VNodeFlags.FRAGMENT,
- null,
- null,
- children,
- childFlags === void 0 ? ChildrenFlags.UNKNOWN_CHILDREN : childFlags,
- key,
- null,
- null
- )
- }
- export function createPortal(
- target: RenderNode | string,
- children: RawChildrenType,
- childFlags?: ChildrenFlags,
- key?: Key | null,
- ref?: Ref | null
- ): VNode {
- return createVNode(
- VNodeFlags.PORTAL,
- target,
- null,
- children,
- childFlags === void 0 ? ChildrenFlags.UNKNOWN_CHILDREN : childFlags,
- key,
- ref,
- null
- )
- }
- export function cloneVNode(vnode: VNode, extraData?: VNodeData): VNode {
- const { flags, data } = vnode
- if (flags & VNodeFlags.ELEMENT || flags & VNodeFlags.COMPONENT) {
- let clonedData = data
- if (extraData != null) {
- clonedData = {}
- if (data != null) {
- for (const key in data) {
- clonedData[key] = data[key]
- }
- }
- if (extraData !== EMPTY_OBJ) {
- for (const key in extraData) {
- if (key === 'class') {
- clonedData.class = normalizeClass([
- clonedData.class,
- extraData.class
- ])
- } else if (key === 'style') {
- clonedData.style = normalizeStyle([
- clonedData.style,
- extraData.style
- ])
- } else if (handlersRE.test(key)) {
- // on*, vnode*
- const existing = clonedData[key]
- clonedData[key] = existing
- ? [].concat(existing, extraData[key])
- : extraData[key]
- } else {
- clonedData[key] = extraData[key]
- }
- }
- }
- }
- const cloned = createVNode(
- flags,
- vnode.tag,
- clonedData,
- vnode.children as RawChildrenType,
- vnode.childFlags,
- vnode.key,
- vnode.ref,
- vnode.slots
- )
- cloned.clonedFrom = vnode.clonedFrom || vnode
- return cloned
- } else if (flags & VNodeFlags.TEXT) {
- return createTextVNode(vnode.children as string)
- } else {
- return vnode
- }
- }
- function normalizeChildren(vnode: VNode, children: any) {
- let childFlags
- if (isArray(children)) {
- const { length } = children
- if (length === 0) {
- childFlags = ChildrenFlags.NO_CHILDREN
- children = null
- } else if (length === 1) {
- childFlags = ChildrenFlags.SINGLE_VNODE
- children = children[0]
- if (children.el) {
- children = cloneVNode(children)
- }
- } else {
- childFlags = ChildrenFlags.KEYED_VNODES
- children = normalizeVNodes(children)
- }
- } else if (children == null) {
- childFlags = ChildrenFlags.NO_CHILDREN
- } else if (children._isVNode) {
- childFlags = ChildrenFlags.SINGLE_VNODE
- if (children.el) {
- children = cloneVNode(children)
- }
- } else {
- // primitives or invalid values, cast to string
- childFlags = ChildrenFlags.SINGLE_VNODE
- children = createTextVNode(children + '')
- }
- vnode.children = children
- vnode.childFlags = childFlags
- }
- export function normalizeVNodes(
- children: any[],
- newChildren: VNode[] = [],
- currentPrefix: string = ''
- ): VNode[] {
- for (let i = 0; i < children.length; i++) {
- const child = children[i]
- let newChild
- if (child == null) {
- newChild = createTextVNode('')
- } else if (child._isVNode) {
- newChild = child.el ? cloneVNode(child) : child
- } else if (isArray(child)) {
- normalizeVNodes(child, newChildren, currentPrefix + i + '|')
- } else {
- newChild = createTextVNode(child + '')
- }
- if (newChild) {
- if (newChild.key == null) {
- newChild.key = currentPrefix + i
- }
- newChildren.push(newChild)
- }
- }
- return newChildren
- }
- // ensure all slot functions return Arrays
- function normalizeSlots(slots: { [name: string]: any }): Slots {
- if (slots._normalized) {
- return slots
- }
- const normalized = { _normalized: true } as any
- for (const name in slots) {
- if (name === STABLE_SLOTS_HINT) {
- continue
- }
- normalized[name] = (...args: any[]) => normalizeSlot(slots[name](...args))
- }
- return normalized
- }
- function normalizeSlot(value: any): VNode[] {
- if (value == null) {
- return [createTextVNode('')]
- } else if (isArray(value)) {
- return normalizeVNodes(value)
- } else if (value._isVNode) {
- return [value]
- } else {
- return [createTextVNode(value + '')]
- }
- }
|