wip: switch to new implementation

This commit is contained in:
Evan You
2019-05-25 23:51:20 +08:00
parent 35effdee5a
commit 3cded86b98
24 changed files with 311 additions and 4412 deletions

View File

@@ -1,168 +1,84 @@
import { ChildrenFlags } from './flags'
import { ComponentClass, FunctionalComponent, Component } from './component'
import { ComponentOptions } from './componentOptions'
import {
VNode,
createElementVNode,
createComponentVNode,
createFragment,
createPortal,
VNodeData,
BuiltInProps,
Key
} from './vdom'
import { isObservable } from '@vue/observer'
import { warn } from './warning'
import { isString, isArray, isFunction, isObject } from '@vue/shared'
export const Fragment = Symbol('Fragment')
export const Text = Symbol('Text')
export const Empty = Symbol('Empty')
export const Fragment = Symbol()
export const Portal = Symbol()
type RawChildType = VNode | string | number | boolean | null | undefined
export type RawSlots = {
$stable?: boolean
[name: string]: RawChildType | (() => RawChildrenType)
}
export type RawChildrenType = RawChildType | RawChildType[]
export type ElementType =
type VNodeTypes =
| string
| FunctionalComponent
| ComponentClass
| ComponentOptions
| Function
| typeof Fragment
| typeof Portal
| typeof Text
| typeof Empty
// This is used to differentiate the data object from
// vnodes and arrays
type Differ = { _isVNode?: never; [Symbol.iterator]?: never }
export type VNodeChild = VNode | string | number | null
export interface VNodeChildren extends Array<VNodeChildren | VNodeChild> {}
type OptionsComponent<P> =
| (ComponentOptions<P> & { template: string })
| (ComponentOptions<P> & { render: Function })
// TODO improve return type with props information
interface createElement {
// element
(tag: string, children?: RawChildrenType): VNode
(
tag: string,
// TODO support native element properties
data?: VNodeData & Differ | null,
children?: RawChildrenType | RawSlots
): VNode
// fragment
(tag: typeof Fragment, children?: RawChildrenType): VNode
(
tag: typeof Fragment,
data?: ({ key?: Key } & Differ) | null,
children?: RawChildrenType | RawSlots
): VNode
// portal
(tag: typeof Portal, children?: RawChildrenType): VNode
(
tag: typeof Portal,
data?: ({ target: any } & BuiltInProps & Differ) | null,
children?: RawChildrenType | RawSlots
): VNode
// object
<P>(tag: OptionsComponent<P>, children?: RawChildrenType): VNode
<P>(
tag: OptionsComponent<P>,
data?: (P & BuiltInProps & Differ) | null,
children?: RawChildrenType | RawSlots
): VNode
// functional
<P>(tag: FunctionalComponent<P>, children?: RawChildrenType): VNode
<P>(
tag: FunctionalComponent<P>,
data?: (P & BuiltInProps & Differ) | null,
children?: RawChildrenType | RawSlots
): VNode
// class
<P>(tag: new () => Component<P>, children?: RawChildrenType): VNode
<P>(
tag: new () => Component<P>,
data?: (P & BuiltInProps & Differ) | null,
children?: RawChildrenType | RawSlots
): VNode
export interface VNode {
type: VNodeTypes
props: { [key: string]: any } | null
key: string | number | null
children: string | VNodeChildren | null
patchFlag: number | null
dynamicProps: string[] | null
dynamicChildren: VNode[] | null
}
export const h = ((tag: ElementType, data?: any, children?: any): VNode => {
if (data !== null && (isArray(data) || !isObject(data) || data._isVNode)) {
children = data
data = null
}
const blockStack: (VNode[])[] = []
if (data === void 0) data = null
if (children === void 0) children = null
// open block
export function openBlock() {
blockStack.push([])
}
// if value is observable, create a clone of original
// so that we can normalize its class/style
// since this guard is only placed here, this means any direct createXXXVnode
// functions only accept fresh data objects.
if (isObservable(data)) {
data = Object.assign({}, data)
}
let shouldTrack = true
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
}
}
// block
export function createBlock(
type: VNodeTypes,
props?: { [key: string]: any } | null,
children?: any,
patchFlag?: number,
dynamicProps?: string[]
): VNode {
// avoid a block with optFlag tracking itself
shouldTrack = false
const vnode = createVNode(type, props, children, patchFlag, dynamicProps)
shouldTrack = true
vnode.dynamicChildren = blockStack.pop() || null
// a block is always going to be patched
trackDynamicNode(vnode)
return vnode
}
if (isString(tag)) {
// element
return createElementVNode(
tag,
data,
children,
ChildrenFlags.UNKNOWN_CHILDREN,
key,
ref
)
} else if (tag === Fragment) {
if (__DEV__ && ref) {
warn('Ref cannot be used on Fragments. Use it on inner elements instead.')
}
return createFragment(children, ChildrenFlags.UNKNOWN_CHILDREN, key)
} else if (tag === Portal) {
if (__DEV__ && !portalTarget) {
warn('Portal must have a target: ', portalTarget)
}
return createPortal(
portalTarget,
children,
ChildrenFlags.UNKNOWN_CHILDREN,
key,
ref
)
} else {
if (__DEV__ && !isFunction(tag) && !isObject(tag)) {
warn('Invalid component passed to h(): ', tag)
}
// component
return createComponentVNode(
tag,
data,
children,
ChildrenFlags.UNKNOWN_CHILDREN,
key,
ref
)
// element
export function createVNode(
type: VNodeTypes,
props: { [key: string]: any } | null = null,
children: any = null,
patchFlag: number | null = null,
dynamicProps: string[] | null = null
): VNode {
const vnode: VNode = {
type,
props,
key: props && props.key,
children,
patchFlag,
dynamicProps,
dynamicChildren: null
}
}) as createElement
if (patchFlag != null && shouldTrack) {
trackDynamicNode(vnode)
}
return vnode
}
function trackDynamicNode(vnode: VNode) {
const currentBlockDynamicNodes = blockStack[blockStack.length - 1]
if (currentBlockDynamicNodes) {
currentBlockDynamicNodes.push(vnode)
}
}
export function cloneVNode(vnode: VNode): VNode {
// TODO
}