mirror of https://github.com/vuejs/core.git
feat(runtime-vapor): implement setupContext (#157)
This commit is contained in:
parent
38e167ceb8
commit
9a2c12e3cd
|
@ -1,15 +1,22 @@
|
|||
import { isArray, isFunction, isObject } from '@vue/shared'
|
||||
import {
|
||||
type ComponentInternalInstance,
|
||||
componentKey,
|
||||
createSetupContext,
|
||||
setCurrentInstance,
|
||||
} from './component'
|
||||
import { insert, querySelector, remove } from './dom/element'
|
||||
import { flushPostFlushCbs, queuePostRenderEffect } from './scheduler'
|
||||
import { proxyRefs } from '@vue/reactivity'
|
||||
import { invokeLifecycle } from './componentLifecycle'
|
||||
import { VaporLifecycleHooks } from './apiLifecycle'
|
||||
import {
|
||||
pauseTracking,
|
||||
proxyRefs,
|
||||
resetTracking,
|
||||
shallowReadonly,
|
||||
} from '@vue/reactivity'
|
||||
import { isArray, isFunction, isObject } from '@vue/shared'
|
||||
import { fallThroughAttrs } from './componentAttrs'
|
||||
import { VaporErrorCodes, callWithErrorHandling } from './errorHandling'
|
||||
|
||||
export const fragmentKey = Symbol(__DEV__ ? `fragmentKey` : ``)
|
||||
|
||||
|
@ -26,11 +33,22 @@ export function setupComponent(
|
|||
): void {
|
||||
const reset = setCurrentInstance(instance)
|
||||
instance.scope.run(() => {
|
||||
const { component, props, emit, attrs } = instance
|
||||
const ctx = { expose: () => {}, emit, attrs }
|
||||
const { component, props } = instance
|
||||
|
||||
const setupFn = isFunction(component) ? component : component.setup
|
||||
const stateOrNode = setupFn && setupFn(props, ctx)
|
||||
let stateOrNode: Block | undefined
|
||||
if (setupFn) {
|
||||
const setupContext = (instance.setupContext =
|
||||
setupFn && setupFn.length > 1 ? createSetupContext(instance) : null)
|
||||
pauseTracking()
|
||||
stateOrNode = callWithErrorHandling(
|
||||
setupFn,
|
||||
instance,
|
||||
VaporErrorCodes.SETUP_FUNCTION,
|
||||
[__DEV__ ? shallowReadonly(props) : props, setupContext],
|
||||
)
|
||||
resetTracking()
|
||||
}
|
||||
|
||||
let block: Block | undefined
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { EffectScope } from '@vue/reactivity'
|
||||
import { EMPTY_OBJ, isFunction } from '@vue/shared'
|
||||
import { EMPTY_OBJ, NOOP, isFunction } from '@vue/shared'
|
||||
import type { Block } from './apiRender'
|
||||
import type { DirectiveBinding } from './directives'
|
||||
import {
|
||||
|
@ -20,12 +20,48 @@ import {
|
|||
import { VaporLifecycleHooks } from './apiLifecycle'
|
||||
|
||||
import type { Data } from '@vue/shared'
|
||||
import { warn } from './warning'
|
||||
|
||||
export type Component = FunctionalComponent | ObjectComponent
|
||||
|
||||
export type SetupFn = (props: any, ctx: any) => Block | Data | void
|
||||
export type SetupFn = (props: any, ctx: SetupContext) => Block | Data | void
|
||||
export type FunctionalComponent = SetupFn & Omit<ObjectComponent, 'setup'>
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface ObjectComponent {
|
||||
props?: ComponentPropsOptions
|
||||
inheritAttrs?: boolean
|
||||
|
@ -59,12 +95,15 @@ export interface ComponentInternalInstance {
|
|||
|
||||
// state
|
||||
setupState: Data
|
||||
setupContext: SetupContext | null
|
||||
props: Data
|
||||
emit: EmitFn
|
||||
emitted: Record<string, boolean> | null
|
||||
attrs: Data
|
||||
refs: Data
|
||||
|
||||
attrsProxy: Data | null
|
||||
|
||||
// lifecycle
|
||||
isMounted: boolean
|
||||
isUnmounted: boolean
|
||||
|
@ -169,12 +208,15 @@ export function createComponentInstance(
|
|||
|
||||
// state
|
||||
setupState: EMPTY_OBJ,
|
||||
setupContext: null,
|
||||
props: EMPTY_OBJ,
|
||||
emit: null!,
|
||||
emitted: null,
|
||||
attrs: EMPTY_OBJ,
|
||||
refs: EMPTY_OBJ,
|
||||
|
||||
attrsProxy: null,
|
||||
|
||||
// lifecycle
|
||||
isMounted: false,
|
||||
isUnmounted: false,
|
||||
|
@ -234,3 +276,31 @@ export function createComponentInstance(
|
|||
|
||||
return instance
|
||||
}
|
||||
|
||||
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]
|
||||
},
|
||||
},
|
||||
))
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue