2018-09-19 23:35:38 +08:00
|
|
|
import { ChildrenFlags } from './flags'
|
2018-10-14 09:13:56 +08:00
|
|
|
import { ComponentClass, FunctionalComponent, Component } 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'
|
2018-10-10 09:10:30 +08:00
|
|
|
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 })
|
|
|
|
|
2018-10-13 10:07:08 +08:00
|
|
|
// 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
|
2018-10-13 10:07:08 +08:00
|
|
|
(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
|
2018-10-13 10:07:08 +08:00
|
|
|
(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
|
2018-10-13 10:07:08 +08:00
|
|
|
(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
|
2018-10-13 10:07:08 +08:00
|
|
|
<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
|
2018-10-13 10:07:08 +08:00
|
|
|
<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
|
2018-10-14 09:13:56 +08:00
|
|
|
<P>(tag: new () => Component<P>, children?: RawChildrenType): VNode
|
2018-10-13 10:07:08 +08:00
|
|
|
<P>(
|
2018-10-14 09:13:56 +08:00
|
|
|
tag: new () => Component<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
|
|
|
|
}
|
|
|
|
|
2018-09-20 09:51:21 +08:00
|
|
|
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
|
|
|
|
|
2018-10-05 04:13:02 +08:00
|
|
|
// if value is observable, create a clone of original
|
2018-10-10 09:10:30 +08:00
|
|
|
// so that we can normalize its class/style
|
2018-10-05 04:13:02 +08:00
|
|
|
if (isObservable(data)) {
|
2018-10-10 09:10:30 +08:00
|
|
|
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
|