2024-01-20 20:46:41 +08:00
|
|
|
import { EffectScope } from '@vue/reactivity'
|
2024-03-22 23:28:18 +08:00
|
|
|
import { EMPTY_OBJ, NOOP, isFunction } from '@vue/shared'
|
2024-03-16 18:54:36 +08:00
|
|
|
import type { Block } from './apiRender'
|
2024-02-25 15:23:29 +08:00
|
|
|
import type { DirectiveBinding } from './directives'
|
2023-12-10 01:33:18 +08:00
|
|
|
import {
|
|
|
|
type ComponentPropsOptions,
|
|
|
|
type NormalizedPropsOptions,
|
2024-03-16 18:54:36 +08:00
|
|
|
type NormalizedRawProps,
|
|
|
|
type RawProps,
|
2024-03-14 16:19:45 +08:00
|
|
|
initProps,
|
2023-12-10 01:33:18 +08:00
|
|
|
normalizePropsOptions,
|
|
|
|
} from './componentProps'
|
2024-02-04 21:18:57 +08:00
|
|
|
import {
|
|
|
|
type EmitFn,
|
|
|
|
type EmitsOptions,
|
|
|
|
type ObjectEmitsOptions,
|
|
|
|
emit,
|
|
|
|
normalizeEmitsOptions,
|
|
|
|
} from './componentEmits'
|
2024-03-14 15:50:58 +08:00
|
|
|
import { VaporLifecycleHooks } from './apiLifecycle'
|
2024-03-22 23:28:18 +08:00
|
|
|
import { warn } from './warning'
|
2024-03-22 23:41:16 +08:00
|
|
|
import { type AppContext, createAppContext } from './apiCreateVaporApp'
|
|
|
|
import type { Data } from '@vue/shared'
|
2023-11-30 02:11:21 +08:00
|
|
|
|
2023-12-10 01:33:18 +08:00
|
|
|
export type Component = FunctionalComponent | ObjectComponent
|
|
|
|
|
2024-03-22 23:28:18 +08:00
|
|
|
export type SetupFn = (props: any, ctx: SetupContext) => Block | Data | void
|
2024-02-14 14:43:18 +08:00
|
|
|
export type FunctionalComponent = SetupFn & Omit<ObjectComponent, 'setup'>
|
|
|
|
|
2024-03-22 23:28:18 +08:00
|
|
|
export type SetupContext<E = EmitsOptions> = E extends any
|
|
|
|
? {
|
|
|
|
attrs: Data
|
|
|
|
emit: EmitFn<E>
|
|
|
|
expose: (exposed?: Record<string, any>) => void
|
|
|
|
// TODO slots
|
|
|
|
}
|
|
|
|
: never
|
|
|
|
|
|
|
|
export function createSetupContext(
|
|
|
|
instance: ComponentInternalInstance,
|
|
|
|
): SetupContext {
|
|
|
|
if (__DEV__) {
|
|
|
|
// We use getters in dev in case libs like test-utils overwrite instance
|
|
|
|
// properties (overwrites should not be done in prod)
|
|
|
|
return Object.freeze({
|
|
|
|
get attrs() {
|
|
|
|
return getAttrsProxy(instance)
|
|
|
|
},
|
|
|
|
get emit() {
|
|
|
|
return (event: string, ...args: any[]) => instance.emit(event, ...args)
|
|
|
|
},
|
|
|
|
expose: NOOP,
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
return {
|
|
|
|
get attrs() {
|
|
|
|
return getAttrsProxy(instance)
|
|
|
|
},
|
|
|
|
emit: instance.emit,
|
|
|
|
expose: NOOP,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-06 14:59:11 +08:00
|
|
|
export interface ObjectComponent {
|
2024-02-14 14:43:18 +08:00
|
|
|
props?: ComponentPropsOptions
|
2024-03-19 00:24:58 +08:00
|
|
|
inheritAttrs?: boolean
|
2024-02-14 14:43:18 +08:00
|
|
|
emits?: EmitsOptions
|
2023-12-13 15:16:33 +08:00
|
|
|
setup?: SetupFn
|
2024-02-14 14:43:18 +08:00
|
|
|
render?(ctx: any): Block
|
|
|
|
vapor?: boolean
|
2023-12-06 14:59:11 +08:00
|
|
|
}
|
|
|
|
|
2023-12-15 01:47:56 +08:00
|
|
|
type LifecycleHook<TFn = Function> = TFn[] | null
|
|
|
|
|
2024-03-19 00:24:58 +08:00
|
|
|
export const componentKey = Symbol(__DEV__ ? `componentKey` : ``)
|
|
|
|
|
2023-11-30 02:11:21 +08:00
|
|
|
export interface ComponentInternalInstance {
|
2024-03-19 00:24:58 +08:00
|
|
|
[componentKey]: true
|
2023-11-30 02:11:21 +08:00
|
|
|
uid: number
|
2024-03-16 18:54:36 +08:00
|
|
|
vapor: true
|
2024-03-22 23:41:16 +08:00
|
|
|
appContext: AppContext
|
2024-03-16 18:54:36 +08:00
|
|
|
|
2023-11-30 02:11:21 +08:00
|
|
|
block: Block | null
|
2024-03-16 18:54:36 +08:00
|
|
|
container: ParentNode
|
|
|
|
parent: ComponentInternalInstance | null
|
|
|
|
|
2024-03-22 23:41:16 +08:00
|
|
|
provides: Data
|
2023-11-30 02:11:21 +08:00
|
|
|
scope: EffectScope
|
2023-12-06 14:59:11 +08:00
|
|
|
component: FunctionalComponent | ObjectComponent
|
2024-03-16 18:54:36 +08:00
|
|
|
comps: Set<ComponentInternalInstance>
|
|
|
|
dirs: Map<Node, DirectiveBinding[]>
|
2024-02-04 21:18:57 +08:00
|
|
|
|
2024-03-16 18:54:36 +08:00
|
|
|
rawProps: NormalizedRawProps
|
2023-12-10 01:33:18 +08:00
|
|
|
propsOptions: NormalizedPropsOptions
|
2024-02-04 21:18:57 +08:00
|
|
|
emitsOptions: ObjectEmitsOptions | null
|
2023-12-10 01:33:18 +08:00
|
|
|
|
|
|
|
// state
|
|
|
|
setupState: Data
|
2024-03-22 23:28:18 +08:00
|
|
|
setupContext: SetupContext | null
|
2024-03-16 18:54:36 +08:00
|
|
|
props: Data
|
2024-02-04 21:18:57 +08:00
|
|
|
emit: EmitFn
|
|
|
|
emitted: Record<string, boolean> | null
|
2024-03-16 18:54:36 +08:00
|
|
|
attrs: Data
|
2024-01-21 13:59:56 +08:00
|
|
|
refs: Data
|
2023-11-30 02:11:21 +08:00
|
|
|
|
2024-03-22 23:28:18 +08:00
|
|
|
attrsProxy: Data | null
|
|
|
|
|
2023-12-10 01:33:18 +08:00
|
|
|
// lifecycle
|
2024-01-20 20:46:41 +08:00
|
|
|
isMounted: boolean
|
|
|
|
isUnmounted: boolean
|
2024-01-13 03:25:57 +08:00
|
|
|
isUpdating: boolean
|
2023-12-23 15:17:18 +08:00
|
|
|
// TODO: registory of provides, lifecycles, ...
|
2023-12-15 01:47:56 +08:00
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
|
|
|
[VaporLifecycleHooks.BEFORE_MOUNT]: LifecycleHook
|
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
|
|
|
[VaporLifecycleHooks.MOUNTED]: LifecycleHook
|
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
|
|
|
[VaporLifecycleHooks.BEFORE_UPDATE]: LifecycleHook
|
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
|
|
|
[VaporLifecycleHooks.UPDATED]: LifecycleHook
|
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
|
|
|
[VaporLifecycleHooks.BEFORE_UNMOUNT]: LifecycleHook
|
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
|
|
|
[VaporLifecycleHooks.UNMOUNTED]: LifecycleHook
|
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
|
|
|
[VaporLifecycleHooks.RENDER_TRACKED]: LifecycleHook
|
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
|
|
|
[VaporLifecycleHooks.RENDER_TRIGGERED]: LifecycleHook
|
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
|
|
|
[VaporLifecycleHooks.ACTIVATED]: LifecycleHook
|
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
|
|
|
[VaporLifecycleHooks.DEACTIVATED]: LifecycleHook
|
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
|
|
|
[VaporLifecycleHooks.ERROR_CAPTURED]: LifecycleHook
|
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
|
|
|
// [VaporLifecycleHooks.SERVER_PREFETCH]: LifecycleHook<() => Promise<unknown>>
|
2023-11-30 02:11:21 +08:00
|
|
|
}
|
|
|
|
|
2023-12-03 18:36:01 +08:00
|
|
|
// TODO
|
|
|
|
export let currentInstance: ComponentInternalInstance | null = null
|
|
|
|
|
|
|
|
export const getCurrentInstance: () => ComponentInternalInstance | null = () =>
|
|
|
|
currentInstance
|
|
|
|
|
|
|
|
export const setCurrentInstance = (instance: ComponentInternalInstance) => {
|
2024-01-19 16:38:41 +08:00
|
|
|
const prev = currentInstance
|
2023-12-03 18:36:01 +08:00
|
|
|
currentInstance = instance
|
2024-01-19 16:38:41 +08:00
|
|
|
instance.scope.on()
|
|
|
|
return () => {
|
|
|
|
instance.scope.off()
|
|
|
|
currentInstance = prev
|
|
|
|
}
|
2023-12-03 18:36:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
export const unsetCurrentInstance = () => {
|
2024-01-19 16:38:41 +08:00
|
|
|
currentInstance?.scope.off()
|
2023-12-03 18:36:01 +08:00
|
|
|
currentInstance = null
|
|
|
|
}
|
|
|
|
|
2024-03-22 23:41:16 +08:00
|
|
|
const emptyAppContext = createAppContext()
|
|
|
|
|
2023-11-30 02:11:21 +08:00
|
|
|
let uid = 0
|
2024-03-16 18:54:36 +08:00
|
|
|
export function createComponentInstance(
|
2023-12-06 14:59:11 +08:00
|
|
|
component: ObjectComponent | FunctionalComponent,
|
2024-03-16 18:54:36 +08:00
|
|
|
rawProps: RawProps | null,
|
2024-03-22 23:41:16 +08:00
|
|
|
// application root node only
|
|
|
|
appContext: AppContext | null = null,
|
2024-03-16 18:54:36 +08:00
|
|
|
): ComponentInternalInstance {
|
2024-03-22 23:41:16 +08:00
|
|
|
const parent = getCurrentInstance()
|
|
|
|
const _appContext =
|
|
|
|
(parent ? parent.appContext : appContext) || emptyAppContext
|
|
|
|
|
2023-11-30 02:11:21 +08:00
|
|
|
const instance: ComponentInternalInstance = {
|
2024-03-19 00:24:58 +08:00
|
|
|
[componentKey]: true,
|
2023-11-30 02:11:21 +08:00
|
|
|
uid: uid++,
|
2024-03-16 18:54:36 +08:00
|
|
|
vapor: true,
|
2024-03-22 23:41:16 +08:00
|
|
|
appContext: _appContext,
|
2024-03-16 18:54:36 +08:00
|
|
|
|
2023-11-30 02:11:21 +08:00
|
|
|
block: null,
|
2024-03-16 18:54:36 +08:00
|
|
|
container: null!,
|
2023-12-10 01:33:18 +08:00
|
|
|
|
2024-03-22 23:41:16 +08:00
|
|
|
parent,
|
2023-12-23 15:17:18 +08:00
|
|
|
|
2024-03-16 18:54:36 +08:00
|
|
|
scope: new EffectScope(true /* detached */)!,
|
2024-03-22 23:41:16 +08:00
|
|
|
provides: parent ? parent.provides : Object.create(_appContext.provides),
|
2024-03-16 18:54:36 +08:00
|
|
|
component,
|
|
|
|
comps: new Set(),
|
|
|
|
dirs: new Map(),
|
|
|
|
|
2023-12-10 01:33:18 +08:00
|
|
|
// resolved props and emits options
|
2024-03-16 18:54:36 +08:00
|
|
|
rawProps: null!, // set later
|
2023-12-10 01:33:18 +08:00
|
|
|
propsOptions: normalizePropsOptions(component),
|
2024-02-04 21:18:57 +08:00
|
|
|
emitsOptions: normalizeEmitsOptions(component),
|
|
|
|
|
2023-12-10 01:33:18 +08:00
|
|
|
// state
|
2024-03-16 18:54:36 +08:00
|
|
|
setupState: EMPTY_OBJ,
|
2024-03-22 23:28:18 +08:00
|
|
|
setupContext: null,
|
2023-12-10 01:33:18 +08:00
|
|
|
props: EMPTY_OBJ,
|
2024-03-16 18:54:36 +08:00
|
|
|
emit: null!,
|
|
|
|
emitted: null,
|
2024-02-10 13:07:13 +08:00
|
|
|
attrs: EMPTY_OBJ,
|
2024-01-21 13:59:56 +08:00
|
|
|
refs: EMPTY_OBJ,
|
2023-12-10 01:33:18 +08:00
|
|
|
|
2024-03-22 23:28:18 +08:00
|
|
|
attrsProxy: null,
|
|
|
|
|
2023-12-10 01:33:18 +08:00
|
|
|
// lifecycle
|
2024-01-20 20:46:41 +08:00
|
|
|
isMounted: false,
|
|
|
|
isUnmounted: false,
|
2024-01-13 03:25:57 +08:00
|
|
|
isUpdating: false,
|
2023-11-30 02:11:21 +08:00
|
|
|
// TODO: registory of provides, appContext, lifecycles, ...
|
2023-12-15 01:47:56 +08:00
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
|
|
|
[VaporLifecycleHooks.BEFORE_MOUNT]: null,
|
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
|
|
|
[VaporLifecycleHooks.MOUNTED]: null,
|
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
|
|
|
[VaporLifecycleHooks.BEFORE_UPDATE]: null,
|
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
|
|
|
[VaporLifecycleHooks.UPDATED]: null,
|
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
|
|
|
[VaporLifecycleHooks.BEFORE_UNMOUNT]: null,
|
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
|
|
|
[VaporLifecycleHooks.UNMOUNTED]: null,
|
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
|
|
|
[VaporLifecycleHooks.RENDER_TRACKED]: null,
|
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
|
|
|
[VaporLifecycleHooks.RENDER_TRIGGERED]: null,
|
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
|
|
|
[VaporLifecycleHooks.ACTIVATED]: null,
|
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
|
|
|
[VaporLifecycleHooks.DEACTIVATED]: null,
|
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
|
|
|
[VaporLifecycleHooks.ERROR_CAPTURED]: null,
|
|
|
|
/**
|
|
|
|
* @internal
|
|
|
|
*/
|
|
|
|
// [VaporLifecycleHooks.SERVER_PREFETCH]: null,
|
2023-11-30 02:11:21 +08:00
|
|
|
}
|
2024-03-14 16:19:45 +08:00
|
|
|
initProps(instance, rawProps, !isFunction(component))
|
2024-02-04 21:18:57 +08:00
|
|
|
instance.emit = emit.bind(null, instance)
|
|
|
|
|
2023-11-30 02:11:21 +08:00
|
|
|
return instance
|
|
|
|
}
|
2024-03-22 23:28:18 +08:00
|
|
|
|
|
|
|
function getAttrsProxy(instance: ComponentInternalInstance): Data {
|
|
|
|
return (
|
|
|
|
instance.attrsProxy ||
|
|
|
|
(instance.attrsProxy = new Proxy(
|
|
|
|
instance.attrs,
|
|
|
|
__DEV__
|
|
|
|
? {
|
|
|
|
get(target, key: string) {
|
|
|
|
return target[key]
|
|
|
|
},
|
|
|
|
set() {
|
|
|
|
warn(`setupContext.attrs is readonly.`)
|
|
|
|
return false
|
|
|
|
},
|
|
|
|
deleteProperty() {
|
|
|
|
warn(`setupContext.attrs is readonly.`)
|
|
|
|
return false
|
|
|
|
},
|
|
|
|
}
|
|
|
|
: {
|
|
|
|
get(target, key: string) {
|
|
|
|
return target[key]
|
|
|
|
},
|
|
|
|
},
|
|
|
|
))
|
|
|
|
)
|
|
|
|
}
|