refactor(runtime-core): refactor instance public proxy context object

This commit is contained in:
Evan You
2020-04-16 12:49:50 -04:00
parent b2662a62c5
commit 370fc820cc
6 changed files with 131 additions and 154 deletions

View File

@@ -83,25 +83,24 @@ const enum AccessTypes {
OTHER
}
export interface ComponentPublicProxyTarget {
export interface ComponentRenderContext {
[key: string]: any
_: ComponentInternalInstance
}
export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
get({ _: instance }: ComponentPublicProxyTarget, key: string) {
get({ _: instance }: ComponentRenderContext, key: string) {
const {
renderContext,
ctx,
setupState,
data,
props,
accessCache,
type,
proxyTarget,
appContext
} = instance
// data / props / renderContext
// data / props / ctx
// This getter gets called for every property access on the render context
// during render and is a major hotspot. The most expensive part of this
// is the multiple hasOwn() calls. It's much faster to do a simple property
@@ -116,7 +115,7 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
case AccessTypes.DATA:
return data[key]
case AccessTypes.CONTEXT:
return renderContext[key]
return ctx[key]
case AccessTypes.PROPS:
return props![key]
// default: just fallthrough
@@ -135,31 +134,30 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
) {
accessCache![key] = AccessTypes.PROPS
return props![key]
} else if (renderContext !== EMPTY_OBJ && hasOwn(renderContext, key)) {
} else if (ctx !== EMPTY_OBJ && hasOwn(ctx, key)) {
accessCache![key] = AccessTypes.CONTEXT
return renderContext[key]
return ctx[key]
} else {
accessCache![key] = AccessTypes.OTHER
}
}
// public $xxx properties &
// user-attached properties (falls through to proxyTarget)
const publicGetter = publicPropertiesMap[key]
let cssModule, globalProperties
// public $xxx properties
if (publicGetter) {
if (__DEV__ && key === '$attrs') {
markAttrsAccessed()
}
return publicGetter(instance)
} else if (hasOwn(proxyTarget, key)) {
return proxyTarget[key]
} else if (
// css module (injected by vue-loader)
(cssModule = type.__cssModules) &&
(cssModule = cssModule[key])
) {
return cssModule
} else if (
// global properties
((globalProperties = appContext.config.globalProperties),
hasOwn(globalProperties, key))
) {
@@ -173,11 +171,11 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
},
set(
{ _: instance }: ComponentPublicProxyTarget,
{ _: instance }: ComponentRenderContext,
key: string,
value: any
): boolean {
const { data, setupState, renderContext } = instance
const { data, setupState, ctx } = instance
if (setupState !== EMPTY_OBJ && hasOwn(setupState, key)) {
setupState[key] = value
} else if (data !== EMPTY_OBJ && hasOwn(data, key)) {
@@ -189,9 +187,8 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
instance
)
return false
} else if (hasOwn(renderContext, key)) {
renderContext[key] = value
} else if (key[0] === '$' && key.slice(1) in instance) {
}
if (key[0] === '$' && key.slice(1) in instance) {
__DEV__ &&
warn(
`Attempting to mutate public property "${key}". ` +
@@ -201,13 +198,13 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
return false
} else {
if (__DEV__ && key in instance.appContext.config.globalProperties) {
Object.defineProperty(instance.proxyTarget, key, {
configurable: true,
Object.defineProperty(ctx, key, {
enumerable: true,
configurable: true,
value
})
} else {
instance.proxyTarget[key] = value
ctx[key] = value
}
}
return true
@@ -215,16 +212,8 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
has(
{
_: {
data,
setupState,
accessCache,
renderContext,
type,
proxyTarget,
appContext
}
}: ComponentPublicProxyTarget,
_: { data, setupState, accessCache, ctx, type, appContext }
}: ComponentRenderContext,
key: string
) {
return (
@@ -232,18 +221,15 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
(data !== EMPTY_OBJ && hasOwn(data, key)) ||
(setupState !== EMPTY_OBJ && hasOwn(setupState, key)) ||
(type.props && hasOwn(normalizePropsOptions(type.props)[0]!, key)) ||
hasOwn(renderContext, key) ||
hasOwn(ctx, key) ||
hasOwn(publicPropertiesMap, key) ||
hasOwn(proxyTarget, key) ||
hasOwn(appContext.config.globalProperties, key)
)
}
}
if (__DEV__ && !__TEST__) {
PublicInstanceProxyHandlers.ownKeys = (
target: ComponentPublicProxyTarget
) => {
PublicInstanceProxyHandlers.ownKeys = (target: ComponentRenderContext) => {
warn(
`Avoid app logic that relies on enumerating keys on a component instance. ` +
`The keys will be empty in production mode to avoid performance overhead.`
@@ -254,14 +240,14 @@ if (__DEV__ && !__TEST__) {
export const RuntimeCompiledPublicInstanceProxyHandlers = {
...PublicInstanceProxyHandlers,
get(target: ComponentPublicProxyTarget, key: string) {
get(target: ComponentRenderContext, key: string) {
// fast path for unscopables when using `with` block
if ((key as any) === Symbol.unscopables) {
return
}
return PublicInstanceProxyHandlers.get!(target, key, target)
},
has(_: ComponentPublicProxyTarget, key: string) {
has(_: ComponentRenderContext, key: string) {
return key[0] !== '_' && !isGloballyWhitelisted(key)
}
}
@@ -269,7 +255,7 @@ export const RuntimeCompiledPublicInstanceProxyHandlers = {
// In dev mode, the proxy target exposes the same properties as seen on `this`
// for easier console inspection. In prod mode it will be an empty object so
// these properties definitions can be skipped.
export function createDevProxyTarget(instance: ComponentInternalInstance) {
export function createRenderContext(instance: ComponentInternalInstance) {
const target: Record<string, any> = {}
// expose internal instance for proxy handlers
@@ -302,19 +288,20 @@ export function createDevProxyTarget(instance: ComponentInternalInstance) {
})
})
return target as ComponentPublicProxyTarget
return target as ComponentRenderContext
}
export function exposePropsOnDevProxyTarget(
// dev only
export function exposePropsOnRenderContext(
instance: ComponentInternalInstance
) {
const {
proxyTarget,
ctx,
type: { props: propsOptions }
} = instance
if (propsOptions) {
Object.keys(normalizePropsOptions(propsOptions)[0]!).forEach(key => {
Object.defineProperty(proxyTarget, key, {
Object.defineProperty(ctx, key, {
enumerable: true,
configurable: true,
get: () => instance.props[key],
@@ -324,12 +311,13 @@ export function exposePropsOnDevProxyTarget(
}
}
export function exposeSetupStateOnDevProxyTarget(
// dev only
export function exposeSetupStateOnRenderContext(
instance: ComponentInternalInstance
) {
const { proxyTarget, setupState } = instance
const { ctx, setupState } = instance
Object.keys(toRaw(setupState)).forEach(key => {
Object.defineProperty(proxyTarget, key, {
Object.defineProperty(ctx, key, {
enumerable: true,
configurable: true,
get: () => setupState[key],