vue3-yuanma/packages/runtime-core/src/component.ts

120 lines
2.8 KiB
TypeScript
Raw Normal View History

2019-05-28 17:19:47 +08:00
import { VNode, normalizeVNode, VNodeChild } from './vnode'
import { ReactiveEffect } from '@vue/observer'
import { isFunction, EMPTY_OBJ } from '@vue/shared'
2019-05-28 13:27:31 +08:00
2019-05-28 17:19:47 +08:00
interface Value<T> {
value: T
}
type UnwrapBindings<T> = {
[key in keyof T]: T[key] extends Value<infer V> ? V : T[key]
}
type Prop<T> = { (): T } | { new (...args: any[]): T & object }
type ExtractPropTypes<PropOptions> = {
readonly [key in keyof PropOptions]: PropOptions[key] extends Prop<infer V>
? V
: PropOptions[key] extends null | undefined ? any : PropOptions[key]
}
interface ComponentPublicProperties<P, S> {
$props: P
$state: S
}
export interface ComponentOptions<
RawProps = { [key: string]: Prop<any> },
RawBindings = { [key: string]: any } | void,
Props = ExtractPropTypes<RawProps>,
Bindings = UnwrapBindings<RawBindings>
> {
props?: RawProps
setup?: (props: Props) => RawBindings
render?: <B extends Bindings>(
this: ComponentPublicProperties<Props, B>,
ctx: {
state: B
props: Props
}
) => VNodeChild
}
2019-05-28 13:27:31 +08:00
2019-05-28 17:19:47 +08:00
// no-op, for type inference only
export function createComponent<
RawProps,
RawBindings,
Props = ExtractPropTypes<RawProps>,
Bindings = UnwrapBindings<RawBindings>
>(
options: ComponentOptions<RawProps, RawBindings, Props, Bindings>
): {
// for TSX
new (): { $props: Props }
} {
return options as any
}
export interface ComponentHandle {
type: Function | ComponentOptions
vnode: VNode | null
next: VNode | null
subTree: VNode | null
update: ReactiveEffect
}
export function renderComponentRoot(handle: ComponentHandle): VNode {
const { type, vnode } = handle
// TODO actually resolve props
const renderArg = {
props: (vnode as VNode).props || EMPTY_OBJ
}
if (isFunction(type)) {
return normalizeVNode(type(renderArg))
} else {
if (__DEV__ && !type.render) {
// TODO warn missing render
}
return normalizeVNode((type.render as Function)(renderArg))
}
2019-05-28 13:27:31 +08:00
}
export function shouldUpdateComponent(
prevVNode: VNode,
nextVNode: VNode
): boolean {
const { props: prevProps } = prevVNode
const { props: nextProps } = nextVNode
// TODO handle slots
// If has different slots content, or has non-compiled slots,
// the child needs to be force updated.
// if (
// prevChildFlags !== nextChildFlags ||
// (nextChildFlags & ChildrenFlags.DYNAMIC_SLOTS) > 0
// ) {
// return true
// }
if (prevProps === nextProps) {
return false
}
if (prevProps === null) {
return nextProps !== null
}
if (nextProps === null) {
return prevProps !== null
}
const nextKeys = Object.keys(nextProps)
if (nextKeys.length !== Object.keys(prevProps).length) {
return true
}
for (let i = 0; i < nextKeys.length; i++) {
const key = nextKeys[i]
if (nextProps[key] !== prevProps[key]) {
return true
}
}
return false
}