wip: vapor warning context integration

This commit is contained in:
Evan You 2024-12-04 10:53:29 +08:00
parent e88c4e2ea7
commit cc2439c9e6
No known key found for this signature in database
GPG Key ID: 00E9AB7A6704CE0A
7 changed files with 83 additions and 36 deletions

View File

@ -120,13 +120,11 @@ export interface App<HostElement = any> {
export type OptionMergeFunction = (to: unknown, from: unknown) => any export type OptionMergeFunction = (to: unknown, from: unknown) => any
export interface AppConfig { /**
// @private * Shared app config between vdom and vapor
readonly isNativeTag: (tag: string) => boolean */
export interface GenericAppConfig {
performance: boolean performance?: boolean
optionMergeStrategies: Record<string, OptionMergeFunction>
globalProperties: ComponentCustomProperties & Record<string, any>
errorHandler?: ( errorHandler?: (
err: unknown, err: unknown,
instance: ComponentPublicInstance | null, instance: ComponentPublicInstance | null,
@ -138,17 +136,6 @@ export interface AppConfig {
trace: string, trace: string,
) => void ) => void
/**
* Options to pass to `@vue/compiler-dom`.
* Only supported in runtime compiler build.
*/
compilerOptions: RuntimeCompilerOptions
/**
* @deprecated use config.compilerOptions.isCustomElement
*/
isCustomElement?: (tag: string) => boolean
/** /**
* TODO document for 3.5 * TODO document for 3.5
* Enable warnings for computed getters that recursively trigger itself. * Enable warnings for computed getters that recursively trigger itself.
@ -168,13 +155,46 @@ export interface AppConfig {
idPrefix?: string idPrefix?: string
} }
export interface AppContext { export interface AppConfig extends GenericAppConfig {
// @private
readonly isNativeTag: (tag: string) => boolean
optionMergeStrategies: Record<string, OptionMergeFunction>
globalProperties: ComponentCustomProperties & Record<string, any>
/**
* Options to pass to `@vue/compiler-dom`.
* Only supported in runtime compiler build.
*/
compilerOptions: RuntimeCompilerOptions
/**
* @deprecated use config.compilerOptions.isCustomElement
*/
isCustomElement?: (tag: string) => boolean
}
/**
* Minimal app context shared between vdom and vapor
*/
export interface GenericAppContext {
app: App // for devtools app: App // for devtools
config: GenericAppConfig
provides: Record<string | symbol, any>
components?: Record<string, Component>
directives?: Record<string, Directive>
/**
* HMR only
* @internal
*/
reload?: () => void
}
export interface AppContext extends GenericAppContext {
config: AppConfig config: AppConfig
mixins: ComponentOptions[]
components: Record<string, Component> components: Record<string, Component>
directives: Record<string, Directive> directives: Record<string, Directive>
provides: Record<string | symbol, any> mixins: ComponentOptions[]
/** /**
* Cache for merged/normalized component options * Cache for merged/normalized component options
@ -193,11 +213,6 @@ export interface AppContext {
* @internal * @internal
*/ */
emitsCache: WeakMap<ConcreteComponent, ObjectEmitsOptions | null> emitsCache: WeakMap<ConcreteComponent, ObjectEmitsOptions | null>
/**
* HMR only
* @internal
*/
reload?: () => void
/** /**
* v2 compat only * v2 compat only
* @internal * @internal

View File

@ -39,6 +39,7 @@ import { ErrorCodes, callWithErrorHandling, handleError } from './errorHandling'
import { import {
type AppConfig, type AppConfig,
type AppContext, type AppContext,
type GenericAppContext,
createAppContext, createAppContext,
} from './apiCreateApp' } from './apiCreateApp'
import { type Directive, validateDirectiveName } from './directives' import { type Directive, validateDirectiveName } from './directives'
@ -328,7 +329,7 @@ export interface GenericComponentInstance {
uid: number uid: number
type: GenericComponent type: GenericComponent
parent: GenericComponentInstance | null parent: GenericComponentInstance | null
appContext: AppContext appContext: GenericAppContext
/** /**
* Object containing values this component provides for its descendants * Object containing values this component provides for its descendants
* @internal * @internal

View File

@ -114,7 +114,6 @@ export function emit(
event: string, event: string,
...rawArgs: any[] ...rawArgs: any[]
): ComponentPublicInstance | null | undefined { ): ComponentPublicInstance | null | undefined {
if (instance.isUnmounted) return
return baseEmit( return baseEmit(
instance, instance,
instance.vnode.props || EMPTY_OBJ, instance.vnode.props || EMPTY_OBJ,
@ -134,6 +133,7 @@ export function baseEmit(
event: string, event: string,
...rawArgs: any[] ...rawArgs: any[]
): ComponentPublicInstance | null | undefined { ): ComponentPublicInstance | null | undefined {
if (instance.isUnmounted) return
if (__DEV__) { if (__DEV__) {
const { emitsOptions, propsOptions } = instance const { emitsOptions, propsOptions } = instance
if (emitsOptions) { if (emitsOptions) {
@ -173,6 +173,8 @@ export function baseEmit(
const isModelListener = event.startsWith('update:') const isModelListener = event.startsWith('update:')
// for v-model update:xxx events, apply modifiers on args // for v-model update:xxx events, apply modifiers on args
// it's ok to use static get because modelModifiers can only be in the static
// part of the props
const modifiers = isModelListener && getModelModifiers(props, event.slice(7)) const modifiers = isModelListener && getModelModifiers(props, event.slice(7))
if (modifiers) { if (modifiers) {
if (modifiers.trim) { if (modifiers.trim) {

View File

@ -240,6 +240,7 @@ export type {
App, App,
AppConfig, AppConfig,
AppContext, AppContext,
GenericAppContext,
Plugin, Plugin,
ObjectPlugin, ObjectPlugin,
FunctionPlugin, FunctionPlugin,
@ -499,3 +500,4 @@ export {
type LifecycleHook, type LifecycleHook,
nextUid, nextUid,
} from './component' } from './component'
export { pushWarningContext, popWarningContext } from './warning'

View File

@ -18,12 +18,18 @@ type TraceEntry = {
type ComponentTraceStack = TraceEntry[] type ComponentTraceStack = TraceEntry[]
/**
* @internal
*/
export function pushWarningContext( export function pushWarningContext(
ctx: GenericComponentInstance | VNode, ctx: GenericComponentInstance | VNode,
): void { ): void {
stack.push(ctx) stack.push(ctx)
} }
/**
* @internal
*/
export function popWarningContext(): void { export function popWarningContext(): void {
stack.pop() stack.pop()
} }

View File

@ -1,14 +1,16 @@
import { import {
type AppContext,
type ComponentInternalOptions, type ComponentInternalOptions,
type ComponentPropsOptions, type ComponentPropsOptions,
EffectScope, EffectScope,
type EmitsOptions, type EmitsOptions,
type GenericAppContext,
type GenericComponentInstance, type GenericComponentInstance,
type LifecycleHook, type LifecycleHook,
type NormalizedPropsOptions, type NormalizedPropsOptions,
type ObjectEmitsOptions, type ObjectEmitsOptions,
nextUid, nextUid,
popWarningContext,
pushWarningContext,
} from '@vue/runtime-core' } from '@vue/runtime-core'
import type { Block } from '../block' import type { Block } from '../block'
import type { Data } from '@vue/runtime-shared' import type { Data } from '@vue/runtime-shared'
@ -86,6 +88,10 @@ export function createComponent(
currentInstance = instance currentInstance = instance
instance.scope.on() instance.scope.on()
if (__DEV__) {
pushWarningContext(instance)
}
const setupFn = isFunction(component) ? component : component.setup const setupFn = isFunction(component) ? component : component.setup
const setupContext = setupFn!.length > 1 ? new SetupContext(instance) : null const setupContext = setupFn!.length > 1 ? new SetupContext(instance) : null
instance.block = setupFn!( instance.block = setupFn!(
@ -108,6 +114,10 @@ export function createComponent(
}) })
} }
if (__DEV__) {
popWarningContext()
}
instance.scope.off() instance.scope.off()
currentInstance = prevInstance currentInstance = prevInstance
resetTracking() resetTracking()
@ -116,11 +126,17 @@ export function createComponent(
export let currentInstance: VaporComponentInstance | null = null export let currentInstance: VaporComponentInstance | null = null
const emptyContext: GenericAppContext = {
app: null as any,
config: {},
provides: /*@__PURE__*/ Object.create(null),
}
export class VaporComponentInstance implements GenericComponentInstance { export class VaporComponentInstance implements GenericComponentInstance {
uid: number uid: number
type: VaporComponent type: VaporComponent
parent: GenericComponentInstance | null parent: GenericComponentInstance | null
appContext: AppContext appContext: GenericAppContext
block: Block block: Block
scope: EffectScope scope: EffectScope
@ -153,8 +169,9 @@ export class VaporComponentInstance implements GenericComponentInstance {
this.uid = nextUid() this.uid = nextUid()
this.type = comp this.type = comp
this.parent = currentInstance this.parent = currentInstance
// @ts-expect-error TODO use proper appContext this.appContext = currentInstance
this.appContext = currentInstance ? currentInstance.appContext : {} ? currentInstance.appContext
: emptyContext
this.block = null! // to be set this.block = null! // to be set
this.scope = new EffectScope(true) this.scope = new EffectScope(true)

View File

@ -8,7 +8,7 @@ import {
type VaporComponentInstance, type VaporComponentInstance,
currentInstance, currentInstance,
} from './component' } from './component'
import { NOOP, hasOwn, isArray } from '@vue/shared' import { EMPTY_OBJ, NOOP, hasOwn, isArray } from '@vue/shared'
import { resolveSource } from './componentProps' import { resolveSource } from './componentProps'
/** /**
@ -48,9 +48,13 @@ export function emit(
event: string, event: string,
...rawArgs: any[] ...rawArgs: any[]
): void { ): void {
const rawProps = instance.rawProps baseEmit(
if (!rawProps || instance.isUnmounted) return instance,
baseEmit(instance, rawProps, propGetter, event, ...rawArgs) instance.rawProps || EMPTY_OBJ,
propGetter,
event,
...rawArgs,
)
} }
function propGetter(rawProps: Record<string, any>, key: string) { function propGetter(rawProps: Record<string, any>, key: string) {