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 {
|
import {
|
||||||
type ComponentInternalInstance,
|
type ComponentInternalInstance,
|
||||||
componentKey,
|
componentKey,
|
||||||
|
createSetupContext,
|
||||||
setCurrentInstance,
|
setCurrentInstance,
|
||||||
} from './component'
|
} from './component'
|
||||||
import { insert, querySelector, remove } from './dom/element'
|
import { insert, querySelector, remove } from './dom/element'
|
||||||
import { flushPostFlushCbs, queuePostRenderEffect } from './scheduler'
|
import { flushPostFlushCbs, queuePostRenderEffect } from './scheduler'
|
||||||
import { proxyRefs } from '@vue/reactivity'
|
|
||||||
import { invokeLifecycle } from './componentLifecycle'
|
import { invokeLifecycle } from './componentLifecycle'
|
||||||
import { VaporLifecycleHooks } from './apiLifecycle'
|
import { VaporLifecycleHooks } from './apiLifecycle'
|
||||||
|
import {
|
||||||
|
pauseTracking,
|
||||||
|
proxyRefs,
|
||||||
|
resetTracking,
|
||||||
|
shallowReadonly,
|
||||||
|
} from '@vue/reactivity'
|
||||||
|
import { isArray, isFunction, isObject } from '@vue/shared'
|
||||||
import { fallThroughAttrs } from './componentAttrs'
|
import { fallThroughAttrs } from './componentAttrs'
|
||||||
|
import { VaporErrorCodes, callWithErrorHandling } from './errorHandling'
|
||||||
|
|
||||||
export const fragmentKey = Symbol(__DEV__ ? `fragmentKey` : ``)
|
export const fragmentKey = Symbol(__DEV__ ? `fragmentKey` : ``)
|
||||||
|
|
||||||
|
@ -26,11 +33,22 @@ export function setupComponent(
|
||||||
): void {
|
): void {
|
||||||
const reset = setCurrentInstance(instance)
|
const reset = setCurrentInstance(instance)
|
||||||
instance.scope.run(() => {
|
instance.scope.run(() => {
|
||||||
const { component, props, emit, attrs } = instance
|
const { component, props } = instance
|
||||||
const ctx = { expose: () => {}, emit, attrs }
|
|
||||||
|
|
||||||
const setupFn = isFunction(component) ? component : component.setup
|
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
|
let block: Block | undefined
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { EffectScope } from '@vue/reactivity'
|
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 { Block } from './apiRender'
|
||||||
import type { DirectiveBinding } from './directives'
|
import type { DirectiveBinding } from './directives'
|
||||||
import {
|
import {
|
||||||
|
@ -20,12 +20,48 @@ import {
|
||||||
import { VaporLifecycleHooks } from './apiLifecycle'
|
import { VaporLifecycleHooks } from './apiLifecycle'
|
||||||
|
|
||||||
import type { Data } from '@vue/shared'
|
import type { Data } from '@vue/shared'
|
||||||
|
import { warn } from './warning'
|
||||||
|
|
||||||
export type Component = FunctionalComponent | ObjectComponent
|
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 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 {
|
export interface ObjectComponent {
|
||||||
props?: ComponentPropsOptions
|
props?: ComponentPropsOptions
|
||||||
inheritAttrs?: boolean
|
inheritAttrs?: boolean
|
||||||
|
@ -59,12 +95,15 @@ export interface ComponentInternalInstance {
|
||||||
|
|
||||||
// state
|
// state
|
||||||
setupState: Data
|
setupState: Data
|
||||||
|
setupContext: SetupContext | null
|
||||||
props: Data
|
props: Data
|
||||||
emit: EmitFn
|
emit: EmitFn
|
||||||
emitted: Record<string, boolean> | null
|
emitted: Record<string, boolean> | null
|
||||||
attrs: Data
|
attrs: Data
|
||||||
refs: Data
|
refs: Data
|
||||||
|
|
||||||
|
attrsProxy: Data | null
|
||||||
|
|
||||||
// lifecycle
|
// lifecycle
|
||||||
isMounted: boolean
|
isMounted: boolean
|
||||||
isUnmounted: boolean
|
isUnmounted: boolean
|
||||||
|
@ -169,12 +208,15 @@ export function createComponentInstance(
|
||||||
|
|
||||||
// state
|
// state
|
||||||
setupState: EMPTY_OBJ,
|
setupState: EMPTY_OBJ,
|
||||||
|
setupContext: null,
|
||||||
props: EMPTY_OBJ,
|
props: EMPTY_OBJ,
|
||||||
emit: null!,
|
emit: null!,
|
||||||
emitted: null,
|
emitted: null,
|
||||||
attrs: EMPTY_OBJ,
|
attrs: EMPTY_OBJ,
|
||||||
refs: EMPTY_OBJ,
|
refs: EMPTY_OBJ,
|
||||||
|
|
||||||
|
attrsProxy: null,
|
||||||
|
|
||||||
// lifecycle
|
// lifecycle
|
||||||
isMounted: false,
|
isMounted: false,
|
||||||
isUnmounted: false,
|
isUnmounted: false,
|
||||||
|
@ -234,3 +276,31 @@ export function createComponentInstance(
|
||||||
|
|
||||||
return instance
|
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