2019-08-31 03:05:39 +08:00
|
|
|
import { VNode, normalizeVNode, VNodeChild, createVNode, Empty } from './vnode'
|
2019-08-23 21:38:32 +08:00
|
|
|
import { ReactiveEffect, UnwrapRef, reactive, readonly } from '@vue/reactivity'
|
2019-09-04 10:25:38 +08:00
|
|
|
import {
|
|
|
|
EMPTY_OBJ,
|
|
|
|
isFunction,
|
|
|
|
capitalize,
|
|
|
|
NOOP,
|
|
|
|
isArray,
|
|
|
|
isObject
|
|
|
|
} from '@vue/shared'
|
2019-05-29 10:43:27 +08:00
|
|
|
import { RenderProxyHandlers } from './componentProxy'
|
2019-05-31 18:07:43 +08:00
|
|
|
import { ComponentPropsOptions, ExtractPropTypes } from './componentProps'
|
|
|
|
import { Slots } from './componentSlots'
|
2019-08-22 23:12:37 +08:00
|
|
|
import { PatchFlags } from './patchFlags'
|
|
|
|
import { ShapeFlags } from './shapeFlags'
|
2019-08-31 03:05:39 +08:00
|
|
|
import { warn } from './warning'
|
|
|
|
import {
|
2019-08-31 03:15:23 +08:00
|
|
|
ErrorTypes,
|
2019-08-31 03:05:39 +08:00
|
|
|
handleError,
|
|
|
|
callWithErrorHandling,
|
|
|
|
callWithAsyncErrorHandling
|
|
|
|
} from './errorHandling'
|
2019-09-04 23:36:27 +08:00
|
|
|
import { AppContext, createAppContext } from './apiApp'
|
2019-09-04 06:11:04 +08:00
|
|
|
import { Directive } from './directives'
|
2019-09-04 23:36:27 +08:00
|
|
|
import { applyOptions, LegacyOptions, resolveAsset } from './apiOptions'
|
2019-05-28 13:27:31 +08:00
|
|
|
|
2019-08-13 23:18:23 +08:00
|
|
|
export type Data = { [key: string]: unknown }
|
2019-05-29 10:43:27 +08:00
|
|
|
|
2019-06-13 10:25:24 +08:00
|
|
|
// public properties exposed on the proxy, which is used as the render context
|
|
|
|
// in templates (as `this` in the render option)
|
2019-06-12 15:43:19 +08:00
|
|
|
export type ComponentRenderProxy<P = {}, S = {}, PublicProps = P> = {
|
2019-06-19 17:08:42 +08:00
|
|
|
$data: S
|
2019-06-12 15:43:19 +08:00
|
|
|
$props: PublicProps
|
2019-05-28 18:06:00 +08:00
|
|
|
$attrs: Data
|
|
|
|
$refs: Data
|
|
|
|
$slots: Data
|
2019-05-29 13:43:46 +08:00
|
|
|
$root: ComponentInstance | null
|
|
|
|
$parent: ComponentInstance | null
|
2019-08-13 23:18:23 +08:00
|
|
|
$emit: (event: string, ...args: unknown[]) => void
|
2019-05-30 13:35:50 +08:00
|
|
|
} & P &
|
|
|
|
S
|
2019-05-28 18:06:00 +08:00
|
|
|
|
2019-06-19 16:43:34 +08:00
|
|
|
type RenderFunction<Props = {}, RawBindings = {}> = <
|
2019-08-16 22:02:53 +08:00
|
|
|
Bindings extends UnwrapRef<RawBindings>
|
2019-06-12 15:43:19 +08:00
|
|
|
>(
|
2019-09-04 03:27:59 +08:00
|
|
|
this: ComponentRenderProxy<Props, Bindings>
|
2019-06-12 15:43:19 +08:00
|
|
|
) => VNodeChild
|
|
|
|
|
2019-09-04 10:25:38 +08:00
|
|
|
interface ComponentOptionsBase<Props, RawBindings> extends LegacyOptions {
|
2019-09-04 06:11:04 +08:00
|
|
|
setup?: (
|
|
|
|
props: Props,
|
|
|
|
ctx: SetupContext
|
|
|
|
) => RawBindings | (() => VNodeChild) | void
|
2019-09-04 10:25:38 +08:00
|
|
|
name?: string
|
|
|
|
template?: string
|
2019-06-19 16:43:34 +08:00
|
|
|
render?: RenderFunction<Props, RawBindings>
|
2019-09-04 06:11:04 +08:00
|
|
|
components?: Record<string, Component>
|
|
|
|
directives?: Record<string, Directive>
|
|
|
|
}
|
|
|
|
|
|
|
|
interface ComponentOptionsWithoutProps<Props = {}, RawBindings = {}>
|
|
|
|
extends ComponentOptionsBase<Props, RawBindings> {
|
|
|
|
props?: undefined
|
2019-06-12 15:43:19 +08:00
|
|
|
}
|
|
|
|
|
2019-06-13 10:25:24 +08:00
|
|
|
interface ComponentOptionsWithArrayProps<
|
|
|
|
PropNames extends string = string,
|
2019-09-04 06:11:04 +08:00
|
|
|
RawBindings = {},
|
2019-08-13 23:18:23 +08:00
|
|
|
Props = { [key in PropNames]?: unknown }
|
2019-09-04 06:11:04 +08:00
|
|
|
> extends ComponentOptionsBase<Props, RawBindings> {
|
2019-06-13 10:25:24 +08:00
|
|
|
props: PropNames[]
|
2019-06-12 15:43:19 +08:00
|
|
|
}
|
|
|
|
|
2019-06-13 10:25:24 +08:00
|
|
|
interface ComponentOptionsWithProps<
|
|
|
|
PropsOptions = ComponentPropsOptions,
|
2019-09-04 06:11:04 +08:00
|
|
|
RawBindings = {},
|
2019-06-13 10:25:24 +08:00
|
|
|
Props = ExtractPropTypes<PropsOptions>
|
2019-09-04 06:11:04 +08:00
|
|
|
> extends ComponentOptionsBase<Props, RawBindings> {
|
2019-06-13 10:25:24 +08:00
|
|
|
props: PropsOptions
|
2019-05-28 17:19:47 +08:00
|
|
|
}
|
2019-05-28 13:27:31 +08:00
|
|
|
|
2019-06-13 10:25:24 +08:00
|
|
|
export type ComponentOptions =
|
2019-06-12 16:22:52 +08:00
|
|
|
| ComponentOptionsWithProps
|
|
|
|
| ComponentOptionsWithoutProps
|
|
|
|
| ComponentOptionsWithArrayProps
|
2019-06-12 15:43:19 +08:00
|
|
|
|
2019-06-19 16:43:34 +08:00
|
|
|
export interface FunctionalComponent<P = {}> {
|
|
|
|
(props: P, ctx: SetupContext): VNodeChild
|
2019-05-28 18:06:00 +08:00
|
|
|
props?: ComponentPropsOptions<P>
|
|
|
|
displayName?: string
|
|
|
|
}
|
|
|
|
|
2019-09-03 04:09:34 +08:00
|
|
|
export type Component = ComponentOptions | FunctionalComponent
|
|
|
|
|
2019-05-28 19:36:15 +08:00
|
|
|
type LifecycleHook = Function[] | null
|
|
|
|
|
2019-08-31 00:16:09 +08:00
|
|
|
export const enum LifecycleHooks {
|
|
|
|
BEFORE_CREATE = 'bc',
|
|
|
|
CREATED = 'c',
|
|
|
|
BEFORE_MOUNT = 'bm',
|
|
|
|
MOUNTED = 'm',
|
|
|
|
BEFORE_UPDATE = 'bu',
|
|
|
|
UPDATED = 'u',
|
|
|
|
BEFORE_UNMOUNT = 'bum',
|
|
|
|
UNMOUNTED = 'um',
|
|
|
|
DEACTIVATED = 'da',
|
|
|
|
ACTIVATED = 'a',
|
|
|
|
RENDER_TRIGGERED = 'rtg',
|
|
|
|
RENDER_TRACKED = 'rtc',
|
|
|
|
ERROR_CAPTURED = 'ec'
|
2019-05-28 19:36:15 +08:00
|
|
|
}
|
|
|
|
|
2019-06-19 16:43:34 +08:00
|
|
|
interface SetupContext {
|
|
|
|
attrs: Data
|
|
|
|
slots: Slots
|
2019-08-13 23:18:23 +08:00
|
|
|
emit: ((event: string, ...args: unknown[]) => void)
|
2019-06-19 16:43:34 +08:00
|
|
|
}
|
|
|
|
|
2019-09-04 06:24:32 +08:00
|
|
|
export type ComponentInstance<P = Data, S = Data> = {
|
2019-05-28 18:06:00 +08:00
|
|
|
type: FunctionalComponent | ComponentOptions
|
2019-06-03 09:43:28 +08:00
|
|
|
parent: ComponentInstance | null
|
2019-09-03 04:09:34 +08:00
|
|
|
appContext: AppContext
|
2019-06-03 09:43:28 +08:00
|
|
|
root: ComponentInstance
|
2019-05-29 10:43:27 +08:00
|
|
|
vnode: VNode
|
2019-05-28 17:19:47 +08:00
|
|
|
next: VNode | null
|
2019-05-29 10:43:27 +08:00
|
|
|
subTree: VNode
|
2019-05-28 17:19:47 +08:00
|
|
|
update: ReactiveEffect
|
2019-06-19 16:43:34 +08:00
|
|
|
render: RenderFunction<P, S> | null
|
2019-06-19 17:31:49 +08:00
|
|
|
effects: ReactiveEffect[] | null
|
2019-06-19 22:48:22 +08:00
|
|
|
provides: Data
|
2019-06-19 16:43:34 +08:00
|
|
|
|
2019-09-04 23:36:27 +08:00
|
|
|
components: Record<string, Component>
|
|
|
|
directives: Record<string, Directive>
|
|
|
|
|
2019-05-29 10:43:27 +08:00
|
|
|
// the rest are only for stateful components
|
2019-06-19 17:08:42 +08:00
|
|
|
data: S
|
2019-05-29 11:36:16 +08:00
|
|
|
props: P
|
2019-06-19 16:43:34 +08:00
|
|
|
renderProxy: ComponentRenderProxy | null
|
|
|
|
propsProxy: P | null
|
|
|
|
setupContext: SetupContext | null
|
2019-09-04 03:27:59 +08:00
|
|
|
refs: Data
|
2019-08-21 21:50:20 +08:00
|
|
|
|
|
|
|
// user namespace
|
|
|
|
user: { [key: string]: any }
|
2019-08-31 00:16:09 +08:00
|
|
|
|
|
|
|
// lifecycle
|
|
|
|
[LifecycleHooks.BEFORE_CREATE]: LifecycleHook
|
|
|
|
[LifecycleHooks.CREATED]: LifecycleHook
|
|
|
|
[LifecycleHooks.BEFORE_MOUNT]: LifecycleHook
|
|
|
|
[LifecycleHooks.MOUNTED]: LifecycleHook
|
|
|
|
[LifecycleHooks.BEFORE_UPDATE]: LifecycleHook
|
|
|
|
[LifecycleHooks.UPDATED]: LifecycleHook
|
|
|
|
[LifecycleHooks.BEFORE_UNMOUNT]: LifecycleHook
|
|
|
|
[LifecycleHooks.UNMOUNTED]: LifecycleHook
|
|
|
|
[LifecycleHooks.RENDER_TRACKED]: LifecycleHook
|
|
|
|
[LifecycleHooks.RENDER_TRIGGERED]: LifecycleHook
|
|
|
|
[LifecycleHooks.ACTIVATED]: LifecycleHook
|
|
|
|
[LifecycleHooks.DEACTIVATED]: LifecycleHook
|
|
|
|
[LifecycleHooks.ERROR_CAPTURED]: LifecycleHook
|
|
|
|
} & SetupContext
|
2019-05-28 19:36:15 +08:00
|
|
|
|
2019-06-13 10:25:24 +08:00
|
|
|
// createComponent
|
|
|
|
// overload 1: direct setup function
|
|
|
|
// (uses user defined props interface)
|
2019-06-12 15:43:19 +08:00
|
|
|
export function createComponent<Props>(
|
2019-08-22 05:10:37 +08:00
|
|
|
setup: (props: Props, ctx: SetupContext) => object | (() => VNodeChild)
|
|
|
|
): (props: Props) => any
|
2019-06-13 10:25:24 +08:00
|
|
|
// overload 2: object format with no props
|
|
|
|
// (uses user defined props interface)
|
|
|
|
// return type is for Vetur and TSX support
|
|
|
|
export function createComponent<Props, RawBindings>(
|
|
|
|
options: ComponentOptionsWithoutProps<Props, RawBindings>
|
|
|
|
): {
|
2019-08-16 22:02:53 +08:00
|
|
|
new (): ComponentRenderProxy<Props, UnwrapRef<RawBindings>>
|
2019-06-13 10:25:24 +08:00
|
|
|
}
|
|
|
|
// overload 3: object format with array props declaration
|
2019-08-13 23:18:23 +08:00
|
|
|
// props inferred as { [key in PropNames]?: unknown }
|
2019-06-13 10:25:24 +08:00
|
|
|
// return type is for Vetur and TSX support
|
2019-06-12 15:43:19 +08:00
|
|
|
export function createComponent<PropNames extends string, RawBindings>(
|
|
|
|
options: ComponentOptionsWithArrayProps<PropNames, RawBindings>
|
|
|
|
): {
|
|
|
|
new (): ComponentRenderProxy<
|
2019-08-13 23:18:23 +08:00
|
|
|
{ [key in PropNames]?: unknown },
|
2019-08-16 22:02:53 +08:00
|
|
|
UnwrapRef<RawBindings>
|
2019-06-12 15:43:19 +08:00
|
|
|
>
|
|
|
|
}
|
2019-06-13 10:25:24 +08:00
|
|
|
// overload 4: object format with object props declaration
|
|
|
|
// see `ExtractPropTypes` in ./componentProps.ts
|
2019-06-12 15:43:19 +08:00
|
|
|
export function createComponent<PropsOptions, RawBindings>(
|
|
|
|
options: ComponentOptionsWithProps<PropsOptions, RawBindings>
|
2019-05-29 10:43:27 +08:00
|
|
|
): {
|
2019-06-12 15:43:19 +08:00
|
|
|
// for Vetur and TSX support
|
|
|
|
new (): ComponentRenderProxy<
|
|
|
|
ExtractPropTypes<PropsOptions>,
|
2019-08-16 22:02:53 +08:00
|
|
|
UnwrapRef<RawBindings>,
|
2019-06-12 15:43:19 +08:00
|
|
|
ExtractPropTypes<PropsOptions, false>
|
|
|
|
>
|
|
|
|
}
|
2019-06-13 10:25:24 +08:00
|
|
|
// implementation, close to no-op
|
2019-06-12 15:43:19 +08:00
|
|
|
export function createComponent(options: any) {
|
|
|
|
return isFunction(options) ? { setup: options } : (options as any)
|
2019-05-29 10:43:27 +08:00
|
|
|
}
|
|
|
|
|
2019-09-03 04:09:34 +08:00
|
|
|
const emptyAppContext = createAppContext()
|
|
|
|
|
2019-06-03 09:43:28 +08:00
|
|
|
export function createComponentInstance(
|
2019-08-29 00:13:36 +08:00
|
|
|
vnode: VNode,
|
2019-06-03 09:43:28 +08:00
|
|
|
parent: ComponentInstance | null
|
|
|
|
): ComponentInstance {
|
2019-09-04 06:11:04 +08:00
|
|
|
// inherit parent app context - or - if root, adopt from root vnode
|
|
|
|
const appContext =
|
|
|
|
(parent ? parent.appContext : vnode.appContext) || emptyAppContext
|
2019-06-03 09:43:28 +08:00
|
|
|
const instance = {
|
2019-08-29 00:13:36 +08:00
|
|
|
vnode,
|
2019-06-03 09:43:28 +08:00
|
|
|
parent,
|
2019-09-04 06:11:04 +08:00
|
|
|
appContext,
|
2019-09-04 23:36:27 +08:00
|
|
|
type: vnode.type as Component,
|
2019-06-03 09:43:28 +08:00
|
|
|
root: null as any, // set later so it can point to itself
|
2019-05-28 19:36:15 +08:00
|
|
|
next: null,
|
2019-05-29 10:43:27 +08:00
|
|
|
subTree: null as any,
|
2019-05-28 19:36:15 +08:00
|
|
|
update: null as any,
|
2019-06-12 15:43:19 +08:00
|
|
|
render: null,
|
2019-05-30 23:16:15 +08:00
|
|
|
renderProxy: null,
|
|
|
|
propsProxy: null,
|
2019-06-19 16:43:34 +08:00
|
|
|
setupContext: null,
|
2019-08-31 00:16:09 +08:00
|
|
|
effects: null,
|
2019-09-04 06:11:04 +08:00
|
|
|
provides: parent ? parent.provides : Object.create(appContext.provides),
|
2019-05-28 19:36:15 +08:00
|
|
|
|
2019-08-31 00:16:09 +08:00
|
|
|
// setup context properties
|
|
|
|
data: EMPTY_OBJ,
|
|
|
|
props: EMPTY_OBJ,
|
|
|
|
attrs: EMPTY_OBJ,
|
|
|
|
slots: EMPTY_OBJ,
|
|
|
|
refs: EMPTY_OBJ,
|
|
|
|
|
2019-09-04 23:36:27 +08:00
|
|
|
// per-instance asset storage (mutable during options resolution)
|
|
|
|
components: Object.create(appContext.components),
|
|
|
|
directives: Object.create(appContext.directives),
|
|
|
|
|
2019-08-31 00:16:09 +08:00
|
|
|
// user namespace for storing whatever the user assigns to `this`
|
|
|
|
user: {},
|
|
|
|
|
|
|
|
// lifecycle hooks
|
|
|
|
// not using enums here because it results in computed properties
|
|
|
|
bc: null,
|
|
|
|
c: null,
|
2019-05-28 19:36:15 +08:00
|
|
|
bm: null,
|
|
|
|
m: null,
|
|
|
|
bu: null,
|
|
|
|
u: null,
|
|
|
|
um: null,
|
|
|
|
bum: null,
|
|
|
|
da: null,
|
|
|
|
a: null,
|
|
|
|
rtg: null,
|
|
|
|
rtc: null,
|
|
|
|
ec: null,
|
2019-08-21 21:50:20 +08:00
|
|
|
|
2019-08-13 23:18:23 +08:00
|
|
|
emit: (event: string, ...args: unknown[]) => {
|
2019-06-19 16:43:34 +08:00
|
|
|
const props = instance.vnode.props || EMPTY_OBJ
|
|
|
|
const handler = props[`on${event}`] || props[`on${capitalize(event)}`]
|
|
|
|
if (handler) {
|
2019-08-31 03:05:39 +08:00
|
|
|
if (isArray(handler)) {
|
|
|
|
for (let i = 0; i < handler.length; i++) {
|
|
|
|
callWithAsyncErrorHandling(
|
|
|
|
handler[i],
|
|
|
|
instance,
|
2019-08-31 03:15:23 +08:00
|
|
|
ErrorTypes.COMPONENT_EVENT_HANDLER,
|
2019-08-31 03:05:39 +08:00
|
|
|
args
|
|
|
|
)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
callWithAsyncErrorHandling(
|
|
|
|
handler,
|
|
|
|
instance,
|
2019-08-31 03:15:23 +08:00
|
|
|
ErrorTypes.COMPONENT_EVENT_HANDLER,
|
2019-08-31 03:05:39 +08:00
|
|
|
args
|
|
|
|
)
|
|
|
|
}
|
2019-06-19 16:43:34 +08:00
|
|
|
}
|
|
|
|
}
|
2019-05-28 19:36:15 +08:00
|
|
|
}
|
2019-06-03 09:43:28 +08:00
|
|
|
|
|
|
|
instance.root = parent ? parent.root : instance
|
|
|
|
return instance
|
2019-05-28 19:36:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
export let currentInstance: ComponentInstance | null = null
|
|
|
|
|
2019-06-20 15:25:10 +08:00
|
|
|
export const getCurrentInstance: () => ComponentInstance | null = () =>
|
|
|
|
currentInstance
|
|
|
|
|
2019-08-31 00:16:09 +08:00
|
|
|
export const setCurrentInstance = (instance: ComponentInstance | null) => {
|
|
|
|
currentInstance = instance
|
|
|
|
}
|
|
|
|
|
2019-05-29 11:36:16 +08:00
|
|
|
export function setupStatefulComponent(instance: ComponentInstance) {
|
2019-09-04 10:25:38 +08:00
|
|
|
currentInstance = instance
|
2019-05-29 10:43:27 +08:00
|
|
|
const Component = instance.type as ComponentOptions
|
2019-05-28 19:36:15 +08:00
|
|
|
// 1. create render proxy
|
2019-06-19 17:08:42 +08:00
|
|
|
instance.renderProxy = new Proxy(instance, RenderProxyHandlers) as any
|
2019-09-04 03:27:59 +08:00
|
|
|
// 2. create props proxy
|
|
|
|
// the propsProxy is a reactive AND readonly proxy to the actual props.
|
|
|
|
// it will be updated in resolveProps() on updates before render
|
|
|
|
const propsProxy = (instance.propsProxy = readonly(instance.props))
|
|
|
|
// 3. call setup()
|
2019-05-30 23:16:15 +08:00
|
|
|
const { setup } = Component
|
|
|
|
if (setup) {
|
2019-06-19 16:43:34 +08:00
|
|
|
const setupContext = (instance.setupContext =
|
|
|
|
setup.length > 1 ? createSetupContext(instance) : null)
|
2019-08-31 03:05:39 +08:00
|
|
|
const setupResult = callWithErrorHandling(
|
|
|
|
setup,
|
|
|
|
instance,
|
2019-08-31 03:15:23 +08:00
|
|
|
ErrorTypes.SETUP_FUNCTION,
|
2019-08-31 03:05:39 +08:00
|
|
|
[propsProxy, setupContext]
|
|
|
|
)
|
2019-06-19 22:48:22 +08:00
|
|
|
|
2019-06-12 15:43:19 +08:00
|
|
|
if (isFunction(setupResult)) {
|
2019-06-13 10:25:24 +08:00
|
|
|
// setup returned an inline render function
|
2019-06-12 15:43:19 +08:00
|
|
|
instance.render = setupResult
|
|
|
|
} else {
|
2019-08-31 04:20:32 +08:00
|
|
|
if (__DEV__) {
|
|
|
|
if (!Component.render) {
|
|
|
|
warn(
|
|
|
|
`Component is missing render function. Either provide a template or ` +
|
|
|
|
`return a render function from setup().`
|
|
|
|
)
|
|
|
|
}
|
|
|
|
if (
|
|
|
|
setupResult &&
|
|
|
|
typeof setupResult.then === 'function' &&
|
|
|
|
typeof setupResult.catch === 'function'
|
|
|
|
) {
|
|
|
|
warn(`setup() returned a Promise. setup() cannot be async.`)
|
|
|
|
}
|
|
|
|
}
|
2019-06-13 10:25:24 +08:00
|
|
|
// setup returned bindings.
|
|
|
|
// assuming a render function compiled from template is present.
|
2019-09-04 10:25:38 +08:00
|
|
|
if (isObject(setupResult)) {
|
2019-09-05 06:16:11 +08:00
|
|
|
instance.data = reactive(setupResult)
|
2019-09-04 10:25:38 +08:00
|
|
|
} else if (__DEV__ && setupResult !== undefined) {
|
|
|
|
warn(
|
|
|
|
`setup() should return an object. Received: ${
|
|
|
|
setupResult === null ? 'null' : typeof setupResult
|
|
|
|
}`
|
|
|
|
)
|
|
|
|
}
|
2019-08-31 03:05:39 +08:00
|
|
|
instance.render = (Component.render || NOOP) as RenderFunction
|
2019-06-12 15:43:19 +08:00
|
|
|
}
|
2019-08-22 05:05:14 +08:00
|
|
|
} else {
|
|
|
|
if (__DEV__ && !Component.render) {
|
2019-09-04 10:25:38 +08:00
|
|
|
warn(
|
|
|
|
`Component is missing render function. Either provide a template or ` +
|
|
|
|
`return a render function from setup().`
|
|
|
|
)
|
2019-08-22 05:05:14 +08:00
|
|
|
}
|
|
|
|
instance.render = Component.render as RenderFunction
|
2019-05-28 19:36:15 +08:00
|
|
|
}
|
2019-09-04 10:25:38 +08:00
|
|
|
// support for 2.x options
|
|
|
|
if (__FEATURE_OPTIONS__) {
|
2019-09-04 23:36:27 +08:00
|
|
|
applyOptions(instance, Component)
|
2019-09-04 10:25:38 +08:00
|
|
|
}
|
2019-09-05 06:16:11 +08:00
|
|
|
if (instance.data === EMPTY_OBJ) {
|
|
|
|
instance.data = reactive({})
|
|
|
|
}
|
2019-09-04 10:25:38 +08:00
|
|
|
currentInstance = null
|
2019-05-28 19:36:15 +08:00
|
|
|
}
|
2019-05-28 17:19:47 +08:00
|
|
|
|
2019-08-23 10:07:51 +08:00
|
|
|
// used to identify a setup context proxy
|
|
|
|
export const SetupProxySymbol = Symbol()
|
|
|
|
|
2019-06-19 16:43:34 +08:00
|
|
|
const SetupProxyHandlers: { [key: string]: ProxyHandler<any> } = {}
|
|
|
|
;['attrs', 'slots', 'refs'].forEach((type: string) => {
|
|
|
|
SetupProxyHandlers[type] = {
|
2019-08-23 10:07:51 +08:00
|
|
|
get: (instance, key) => (instance[type] as any)[key],
|
|
|
|
has: (instance, key) =>
|
|
|
|
key === SetupProxySymbol || key in (instance[type] as any),
|
|
|
|
ownKeys: instance => Reflect.ownKeys(instance[type] as any),
|
|
|
|
// this is necessary for ownKeys to work properly
|
|
|
|
getOwnPropertyDescriptor: (instance, key) =>
|
|
|
|
Reflect.getOwnPropertyDescriptor(instance[type], key),
|
2019-06-19 16:43:34 +08:00
|
|
|
set: () => false,
|
|
|
|
deleteProperty: () => false
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
function createSetupContext(instance: ComponentInstance): SetupContext {
|
|
|
|
const context = {
|
|
|
|
// attrs, slots & refs are non-reactive, but they need to always expose
|
|
|
|
// the latest values (instance.xxx may get replaced during updates) so we
|
|
|
|
// need to expose them through a proxy
|
|
|
|
attrs: new Proxy(instance, SetupProxyHandlers.attrs),
|
|
|
|
slots: new Proxy(instance, SetupProxyHandlers.slots),
|
|
|
|
refs: new Proxy(instance, SetupProxyHandlers.refs),
|
2019-08-27 06:08:56 +08:00
|
|
|
emit: instance.emit
|
2019-06-19 16:43:34 +08:00
|
|
|
} as any
|
|
|
|
return __DEV__ ? Object.freeze(context) : context
|
|
|
|
}
|
|
|
|
|
2019-09-01 04:36:36 +08:00
|
|
|
// mark the current rendering instance for asset resolution (e.g.
|
|
|
|
// resolveComponent, resolveDirective) during render
|
|
|
|
export let currentRenderingInstance: ComponentInstance | null = null
|
|
|
|
|
2019-05-29 11:36:16 +08:00
|
|
|
export function renderComponentRoot(instance: ComponentInstance): VNode {
|
2019-06-19 16:43:34 +08:00
|
|
|
const {
|
|
|
|
type: Component,
|
|
|
|
vnode,
|
|
|
|
renderProxy,
|
|
|
|
props,
|
|
|
|
slots,
|
|
|
|
attrs,
|
2019-08-27 06:08:56 +08:00
|
|
|
emit
|
2019-06-19 16:43:34 +08:00
|
|
|
} = instance
|
2019-09-01 04:36:36 +08:00
|
|
|
|
|
|
|
let result
|
|
|
|
currentRenderingInstance = instance
|
2019-08-31 03:05:39 +08:00
|
|
|
try {
|
|
|
|
if (vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT) {
|
2019-09-01 04:36:36 +08:00
|
|
|
result = normalizeVNode(
|
2019-09-04 03:27:59 +08:00
|
|
|
(instance.render as RenderFunction).call(renderProxy)
|
2019-08-31 03:05:39 +08:00
|
|
|
)
|
|
|
|
} else {
|
|
|
|
// functional
|
|
|
|
const render = Component as FunctionalComponent
|
2019-09-01 04:36:36 +08:00
|
|
|
result = normalizeVNode(
|
2019-08-31 03:05:39 +08:00
|
|
|
render.length > 1
|
|
|
|
? render(props, {
|
|
|
|
attrs,
|
|
|
|
slots,
|
|
|
|
emit
|
|
|
|
})
|
|
|
|
: render(props, null as any)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
} catch (err) {
|
2019-08-31 03:15:23 +08:00
|
|
|
handleError(err, instance, ErrorTypes.RENDER_FUNCTION)
|
2019-09-01 04:36:36 +08:00
|
|
|
result = createVNode(Empty)
|
2019-05-28 17:19:47 +08:00
|
|
|
}
|
2019-09-01 04:36:36 +08:00
|
|
|
currentRenderingInstance = null
|
|
|
|
return result
|
2019-05-28 13:27:31 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
export function shouldUpdateComponent(
|
|
|
|
prevVNode: VNode,
|
2019-06-01 02:14:49 +08:00
|
|
|
nextVNode: VNode,
|
|
|
|
optimized?: boolean
|
2019-05-28 13:27:31 +08:00
|
|
|
): boolean {
|
2019-05-31 12:25:11 +08:00
|
|
|
const { props: prevProps, children: prevChildren } = prevVNode
|
|
|
|
const { props: nextProps, children: nextChildren, patchFlag } = nextVNode
|
2019-06-03 09:43:28 +08:00
|
|
|
if (patchFlag) {
|
2019-08-22 23:12:37 +08:00
|
|
|
if (patchFlag & PatchFlags.DYNAMIC_SLOTS) {
|
2019-05-30 16:00:42 +08:00
|
|
|
// slot content that references values that might have changed,
|
|
|
|
// e.g. in a v-for
|
|
|
|
return true
|
|
|
|
}
|
2019-08-22 23:12:37 +08:00
|
|
|
if (patchFlag & PatchFlags.FULL_PROPS) {
|
2019-05-31 12:25:11 +08:00
|
|
|
// presence of this flag indicates props are always non-null
|
|
|
|
return hasPropsChanged(prevProps as Data, nextProps as Data)
|
2019-08-22 23:12:37 +08:00
|
|
|
} else if (patchFlag & PatchFlags.PROPS) {
|
2019-05-30 16:00:42 +08:00
|
|
|
const dynamicProps = nextVNode.dynamicProps as string[]
|
|
|
|
for (let i = 0; i < dynamicProps.length; i++) {
|
|
|
|
const key = dynamicProps[i]
|
|
|
|
if ((nextProps as any)[key] !== (prevProps as any)[key]) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-06-01 02:14:49 +08:00
|
|
|
} else if (!optimized) {
|
2019-05-31 12:25:11 +08:00
|
|
|
// this path is only taken by manually written render functions
|
|
|
|
// so presence of any children leads to a forced update
|
|
|
|
if (prevChildren != null || nextChildren != null) {
|
|
|
|
return true
|
|
|
|
}
|
2019-05-30 16:00:42 +08:00
|
|
|
if (prevProps === nextProps) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
if (prevProps === null) {
|
|
|
|
return nextProps !== null
|
|
|
|
}
|
|
|
|
if (nextProps === null) {
|
|
|
|
return prevProps !== null
|
|
|
|
}
|
2019-05-31 12:25:11 +08:00
|
|
|
return hasPropsChanged(prevProps, nextProps)
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
function hasPropsChanged(prevProps: Data, nextProps: Data): boolean {
|
|
|
|
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]) {
|
2019-05-28 13:27:31 +08:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
2019-09-03 04:43:26 +08:00
|
|
|
|
|
|
|
export function resolveComponent(name: string): Component | undefined {
|
2019-09-04 23:36:27 +08:00
|
|
|
return resolveAsset('components', name) as any
|
2019-09-03 04:43:26 +08:00
|
|
|
}
|