vue3-yuanma/packages/core/src/h.ts

190 lines
4.5 KiB
TypeScript
Raw Normal View History

2018-09-19 23:35:38 +08:00
import { ChildrenFlags } from './flags'
2018-10-13 01:42:19 +08:00
import {
ComponentClass,
FunctionalComponent,
ComponentInstance
} from './component'
2018-09-19 23:35:38 +08:00
import { ComponentOptions } from './componentOptions'
import {
VNode,
createElementVNode,
createComponentVNode,
createTextVNode,
createFragment,
2018-10-13 01:42:19 +08:00
createPortal,
VNodeData,
2018-10-13 07:49:41 +08:00
BuiltInProps,
Key
2018-09-19 23:35:38 +08:00
} from './vdom'
import { isObservable } from '@vue/observer'
2018-10-12 05:21:13 +08:00
import { warn } from './warning'
2018-09-19 23:35:38 +08:00
export const Fragment = Symbol()
export const Portal = Symbol()
2018-10-13 07:49:41 +08:00
type RawChildType = VNode | string | number | boolean | null | undefined
export type RawSlots = {
[name: string]: () => RawChildrenType
}
export type RawChildrenType = RawChildType | RawChildType[]
2018-10-13 01:42:19 +08:00
export type ElementType =
2018-09-19 23:35:38 +08:00
| string
| FunctionalComponent
| ComponentClass
| ComponentOptions
| typeof Fragment
| typeof Portal
2018-10-13 01:42:19 +08:00
interface VNodeFactories {
2018-09-19 23:35:38 +08:00
c: typeof createComponentVNode
e: typeof createElementVNode
t: typeof createTextVNode
f: typeof createFragment
p: typeof createPortal
}
2018-10-13 07:49:41 +08:00
// This is used to differentiate the data object from
// vnodes and arrays
type Differ = { _isVNode?: never; [Symbol.iterator]?: never }
type OptionsComponent<P> =
| (ComponentOptions<P> & { template: string })
| (ComponentOptions<P> & { render: Function })
// TODO improve return type with props information
2018-10-13 07:49:41 +08:00
interface createElement extends VNodeFactories {
2018-10-13 01:42:19 +08:00
// element
(tag: string, children?: RawChildrenType): VNode
2018-10-13 07:49:41 +08:00
(
tag: string,
// TODO support native element properties
data?: VNodeData & Differ | null,
children?: RawChildrenType | RawSlots
): VNode
// fragment
(tag: typeof Fragment, children?: RawChildrenType): VNode
2018-10-13 07:49:41 +08:00
(
tag: typeof Fragment,
data?: ({ key?: Key } & Differ) | null,
children?: RawChildrenType | RawSlots
): VNode
// portal
(tag: typeof Portal, children?: RawChildrenType): VNode
2018-10-13 07:49:41 +08:00
(
tag: typeof Portal,
data?: ({ target: any } & BuiltInProps & Differ) | null,
children?: RawChildrenType | RawSlots
): VNode
// object
<P>(tag: OptionsComponent<P>, children?: RawChildrenType): VNode
2018-10-13 07:49:41 +08:00
<P>(
tag: OptionsComponent<P>,
data?: (P & BuiltInProps & Differ) | null,
children?: RawChildrenType | RawSlots
): VNode
2018-10-13 01:42:19 +08:00
// functional
<P>(tag: FunctionalComponent<P>, children?: RawChildrenType): VNode
2018-10-13 01:42:19 +08:00
<P>(
tag: FunctionalComponent<P>,
2018-10-13 07:49:41 +08:00
data?: (P & BuiltInProps & Differ) | null,
children?: RawChildrenType | RawSlots
): VNode
// class
<P>(tag: new () => ComponentInstance<P>, children?: RawChildrenType): VNode
<P>(
tag: new () => ComponentInstance<P>,
2018-10-13 07:49:41 +08:00
data?: (P & BuiltInProps & Differ) | null,
children?: RawChildrenType | RawSlots
2018-10-13 01:42:19 +08:00
): VNode
}
export const h = ((tag: ElementType, data?: any, children?: any): VNode => {
2018-09-26 05:49:47 +08:00
if (
Array.isArray(data) ||
(data != null && (typeof data !== 'object' || data._isVNode))
) {
2018-09-19 23:35:38 +08:00
children = data
data = null
}
2018-09-25 07:11:14 +08:00
if (data === void 0) data = null
if (children === void 0) children = null
// if value is observable, create a clone of original
// so that we can normalize its class/style
if (isObservable(data)) {
data = Object.assign({}, data)
2018-09-25 07:11:14 +08:00
}
2018-09-19 23:35:38 +08:00
let key = null
let ref = null
let portalTarget = null
if (data != null) {
if (data.slots != null) {
children = data.slots
}
if (data.key != null) {
;({ key } = data)
}
if (data.ref != null) {
;({ ref } = data)
}
if (data.target != null) {
portalTarget = data.target
}
}
if (typeof tag === 'string') {
// element
return createElementVNode(
tag,
data,
children,
ChildrenFlags.UNKNOWN_CHILDREN,
key,
ref
)
} else if (tag === Fragment) {
if (__DEV__ && ref) {
2018-10-12 05:21:13 +08:00
warn('Ref cannot be used on Fragments. Use it on inner elements instead.')
2018-09-19 23:35:38 +08:00
}
return createFragment(children, ChildrenFlags.UNKNOWN_CHILDREN, key)
} else if (tag === Portal) {
if (__DEV__ && !portalTarget) {
2018-10-12 05:21:13 +08:00
warn('Portal must have a target: ', portalTarget)
2018-09-19 23:35:38 +08:00
}
return createPortal(
portalTarget,
children,
ChildrenFlags.UNKNOWN_CHILDREN,
key,
ref
)
} else {
2018-09-27 06:34:21 +08:00
if (
__DEV__ &&
(!tag || (typeof tag !== 'function' && typeof tag !== 'object'))
) {
2018-10-12 05:21:13 +08:00
warn('Invalid component passed to h(): ', tag)
2018-09-27 06:34:21 +08:00
}
2018-09-19 23:35:38 +08:00
// component
return createComponentVNode(
tag,
data,
children,
ChildrenFlags.UNKNOWN_CHILDREN,
key,
ref
)
}
2018-10-13 07:49:41 +08:00
}) as createElement
2018-09-19 23:35:38 +08:00
h.c = createComponentVNode
h.e = createElementVNode
h.t = createTextVNode
h.f = createFragment
h.p = createPortal