refactor(runtime-core): refactor instance public proxy context object
This commit is contained in:
parent
b2662a62c5
commit
370fc820cc
@ -116,7 +116,7 @@ describe('component: proxy', () => {
|
|||||||
render(h(Comp), nodeOps.createElement('div'))
|
render(h(Comp), nodeOps.createElement('div'))
|
||||||
instanceProxy.foo = 1
|
instanceProxy.foo = 1
|
||||||
expect(instanceProxy.foo).toBe(1)
|
expect(instanceProxy.foo).toBe(1)
|
||||||
expect(instance!.proxyTarget.foo).toBe(1)
|
expect(instance!.ctx.foo).toBe(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('globalProperties', () => {
|
test('globalProperties', () => {
|
||||||
@ -141,7 +141,7 @@ describe('component: proxy', () => {
|
|||||||
// set should overwrite globalProperties with local
|
// set should overwrite globalProperties with local
|
||||||
instanceProxy.foo = 2
|
instanceProxy.foo = 2
|
||||||
// expect(instanceProxy.foo).toBe(2)
|
// expect(instanceProxy.foo).toBe(2)
|
||||||
expect(instance!.proxyTarget.foo).toBe(2)
|
expect(instance!.ctx.foo).toBe(2)
|
||||||
// should not affect global
|
// should not affect global
|
||||||
expect(app.config.globalProperties.foo).toBe(1)
|
expect(app.config.globalProperties.foo).toBe(1)
|
||||||
})
|
})
|
||||||
@ -177,7 +177,7 @@ describe('component: proxy', () => {
|
|||||||
expect('msg' in instanceProxy).toBe(true)
|
expect('msg' in instanceProxy).toBe(true)
|
||||||
// data
|
// data
|
||||||
expect('foo' in instanceProxy).toBe(true)
|
expect('foo' in instanceProxy).toBe(true)
|
||||||
// renderContext
|
// ctx
|
||||||
expect('bar' in instanceProxy).toBe(true)
|
expect('bar' in instanceProxy).toBe(true)
|
||||||
// public properties
|
// public properties
|
||||||
expect('$el' in instanceProxy).toBe(true)
|
expect('$el' in instanceProxy).toBe(true)
|
||||||
|
@ -8,12 +8,11 @@ import {
|
|||||||
} from '@vue/reactivity'
|
} from '@vue/reactivity'
|
||||||
import {
|
import {
|
||||||
ComponentPublicInstance,
|
ComponentPublicInstance,
|
||||||
ComponentPublicProxyTarget,
|
|
||||||
PublicInstanceProxyHandlers,
|
PublicInstanceProxyHandlers,
|
||||||
RuntimeCompiledPublicInstanceProxyHandlers,
|
RuntimeCompiledPublicInstanceProxyHandlers,
|
||||||
createDevProxyTarget,
|
createRenderContext,
|
||||||
exposePropsOnDevProxyTarget,
|
exposePropsOnRenderContext,
|
||||||
exposeSetupStateOnDevProxyTarget
|
exposeSetupStateOnRenderContext
|
||||||
} from './componentProxy'
|
} from './componentProxy'
|
||||||
import { ComponentPropsOptions, initProps } from './componentProps'
|
import { ComponentPropsOptions, initProps } from './componentProps'
|
||||||
import { Slots, initSlots, InternalSlots } from './componentSlots'
|
import { Slots, initSlots, InternalSlots } from './componentSlots'
|
||||||
@ -136,13 +135,23 @@ export interface ComponentInternalInstance {
|
|||||||
components: Record<string, Component>
|
components: Record<string, Component>
|
||||||
directives: Record<string, Directive>
|
directives: Record<string, Directive>
|
||||||
|
|
||||||
// the rest are only for stateful components
|
// the rest are only for stateful components ---------------------------------
|
||||||
renderContext: Data
|
|
||||||
|
// main proxy that serves as the public instance (`this`)
|
||||||
|
proxy: ComponentPublicInstance | null
|
||||||
|
// alternative proxy used only for runtime-compiled render functions using
|
||||||
|
// `with` block
|
||||||
|
withProxy: ComponentPublicInstance | null
|
||||||
|
// This is the target for the public instance proxy. It also holds properties
|
||||||
|
// injected by user options (computed, methods etc.) and user-attached
|
||||||
|
// custom properties (via `this.x = ...`)
|
||||||
|
ctx: Data
|
||||||
|
|
||||||
|
// internal state
|
||||||
data: Data
|
data: Data
|
||||||
props: Data
|
props: Data
|
||||||
attrs: Data
|
attrs: Data
|
||||||
slots: InternalSlots
|
slots: InternalSlots
|
||||||
proxy: ComponentPublicInstance | null
|
|
||||||
refs: Data
|
refs: Data
|
||||||
emit: EmitFn
|
emit: EmitFn
|
||||||
|
|
||||||
@ -150,16 +159,6 @@ export interface ComponentInternalInstance {
|
|||||||
setupState: Data
|
setupState: Data
|
||||||
setupContext: SetupContext | null
|
setupContext: SetupContext | null
|
||||||
|
|
||||||
// The target object for the public instance proxy. In dev mode, we also
|
|
||||||
// define getters for all known instance properties on it so it can be
|
|
||||||
// properly inspected in the console. These getters are skipped in prod mode
|
|
||||||
// for performance. In addition, any user attached properties
|
|
||||||
// (via `this.x = ...`) are also stored on this object.
|
|
||||||
proxyTarget: ComponentPublicProxyTarget
|
|
||||||
// alternative proxy used only for runtime-compiled render functions using
|
|
||||||
// `with` block
|
|
||||||
withProxy: ComponentPublicInstance | null
|
|
||||||
|
|
||||||
// suspense related
|
// suspense related
|
||||||
suspense: SuspenseBoundary | null
|
suspense: SuspenseBoundary | null
|
||||||
asyncDep: Promise<any> | null
|
asyncDep: Promise<any> | null
|
||||||
@ -211,7 +210,6 @@ export function createComponentInstance(
|
|||||||
update: null!, // will be set synchronously right after creation
|
update: null!, // will be set synchronously right after creation
|
||||||
render: null,
|
render: null,
|
||||||
proxy: null,
|
proxy: null,
|
||||||
proxyTarget: null!, // to be immediately set
|
|
||||||
withProxy: null,
|
withProxy: null,
|
||||||
effects: null,
|
effects: null,
|
||||||
provides: parent ? parent.provides : Object.create(appContext.provides),
|
provides: parent ? parent.provides : Object.create(appContext.provides),
|
||||||
@ -219,7 +217,7 @@ export function createComponentInstance(
|
|||||||
renderCache: [],
|
renderCache: [],
|
||||||
|
|
||||||
// state
|
// state
|
||||||
renderContext: EMPTY_OBJ,
|
ctx: EMPTY_OBJ,
|
||||||
data: EMPTY_OBJ,
|
data: EMPTY_OBJ,
|
||||||
props: EMPTY_OBJ,
|
props: EMPTY_OBJ,
|
||||||
attrs: EMPTY_OBJ,
|
attrs: EMPTY_OBJ,
|
||||||
@ -258,9 +256,9 @@ export function createComponentInstance(
|
|||||||
emit: null as any // to be set immediately
|
emit: null as any // to be set immediately
|
||||||
}
|
}
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
instance.proxyTarget = createDevProxyTarget(instance)
|
instance.ctx = createRenderContext(instance)
|
||||||
} else {
|
} else {
|
||||||
instance.proxyTarget = { _: instance }
|
instance.ctx = { _: instance }
|
||||||
}
|
}
|
||||||
instance.root = parent ? parent.root : instance
|
instance.root = parent ? parent.root : instance
|
||||||
instance.emit = emit.bind(null, instance)
|
instance.emit = emit.bind(null, instance)
|
||||||
@ -335,9 +333,9 @@ function setupStatefulComponent(
|
|||||||
// 0. create render proxy property access cache
|
// 0. create render proxy property access cache
|
||||||
instance.accessCache = {}
|
instance.accessCache = {}
|
||||||
// 1. create public instance / render proxy
|
// 1. create public instance / render proxy
|
||||||
instance.proxy = new Proxy(instance.proxyTarget, PublicInstanceProxyHandlers)
|
instance.proxy = new Proxy(instance.ctx, PublicInstanceProxyHandlers)
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
exposePropsOnDevProxyTarget(instance)
|
exposePropsOnRenderContext(instance)
|
||||||
}
|
}
|
||||||
// 2. call setup()
|
// 2. call setup()
|
||||||
const { setup } = Component
|
const { setup } = Component
|
||||||
@ -399,7 +397,7 @@ export function handleSetupResult(
|
|||||||
// assuming a render function compiled from template is present.
|
// assuming a render function compiled from template is present.
|
||||||
instance.setupState = reactive(setupResult)
|
instance.setupState = reactive(setupResult)
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
exposeSetupStateOnDevProxyTarget(instance)
|
exposeSetupStateOnRenderContext(instance)
|
||||||
}
|
}
|
||||||
} else if (__DEV__ && setupResult !== undefined) {
|
} else if (__DEV__ && setupResult !== undefined) {
|
||||||
warn(
|
warn(
|
||||||
@ -469,7 +467,7 @@ function finishComponentSetup(
|
|||||||
// also only allows a whitelist of globals to fallthrough.
|
// also only allows a whitelist of globals to fallthrough.
|
||||||
if (instance.render._rc) {
|
if (instance.render._rc) {
|
||||||
instance.withProxy = new Proxy(
|
instance.withProxy = new Proxy(
|
||||||
instance.proxyTarget,
|
instance.ctx,
|
||||||
RuntimeCompiledPublicInstanceProxyHandlers
|
RuntimeCompiledPublicInstanceProxyHandlers
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -39,9 +39,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
reactive,
|
reactive,
|
||||||
ComputedGetter,
|
ComputedGetter,
|
||||||
WritableComputedOptions,
|
WritableComputedOptions
|
||||||
ComputedRef,
|
|
||||||
toRaw
|
|
||||||
} from '@vue/reactivity'
|
} from '@vue/reactivity'
|
||||||
import {
|
import {
|
||||||
ComponentObjectPropsOptions,
|
ComponentObjectPropsOptions,
|
||||||
@ -246,8 +244,7 @@ export function applyOptions(
|
|||||||
options: ComponentOptions,
|
options: ComponentOptions,
|
||||||
asMixin: boolean = false
|
asMixin: boolean = false
|
||||||
) {
|
) {
|
||||||
const proxyTarget = instance.proxyTarget
|
const publicThis = instance.proxy!
|
||||||
const ctx = instance.proxy!
|
|
||||||
const {
|
const {
|
||||||
// composition
|
// composition
|
||||||
mixins,
|
mixins,
|
||||||
@ -277,19 +274,13 @@ export function applyOptions(
|
|||||||
errorCaptured
|
errorCaptured
|
||||||
} = options
|
} = options
|
||||||
|
|
||||||
const renderContext = toRaw(
|
const ctx = instance.ctx
|
||||||
instance.renderContext === EMPTY_OBJ &&
|
|
||||||
(computedOptions || methods || watchOptions || injectOptions)
|
|
||||||
? (instance.renderContext = reactive({}))
|
|
||||||
: instance.renderContext
|
|
||||||
)
|
|
||||||
|
|
||||||
const globalMixins = instance.appContext.mixins
|
const globalMixins = instance.appContext.mixins
|
||||||
// call it only during dev
|
// call it only during dev
|
||||||
|
|
||||||
// applyOptions is called non-as-mixin once per instance
|
// applyOptions is called non-as-mixin once per instance
|
||||||
if (!asMixin) {
|
if (!asMixin) {
|
||||||
callSyncHook('beforeCreate', options, ctx, globalMixins)
|
callSyncHook('beforeCreate', options, publicThis, globalMixins)
|
||||||
// global mixins are applied first
|
// global mixins are applied first
|
||||||
applyMixins(instance, globalMixins)
|
applyMixins(instance, globalMixins)
|
||||||
}
|
}
|
||||||
@ -318,7 +309,7 @@ export function applyOptions(
|
|||||||
`Plain object usage is no longer supported.`
|
`Plain object usage is no longer supported.`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
const data = dataOptions.call(ctx, ctx)
|
const data = dataOptions.call(publicThis, publicThis)
|
||||||
if (__DEV__ && isPromise(data)) {
|
if (__DEV__ && isPromise(data)) {
|
||||||
warn(
|
warn(
|
||||||
`data() returned a Promise - note data() cannot be async; If you ` +
|
`data() returned a Promise - note data() cannot be async; If you ` +
|
||||||
@ -332,7 +323,13 @@ export function applyOptions(
|
|||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
for (const key in data) {
|
for (const key in data) {
|
||||||
checkDuplicateProperties!(OptionTypes.DATA, key)
|
checkDuplicateProperties!(OptionTypes.DATA, key)
|
||||||
if (!(key in proxyTarget)) proxyTarget[key] = data[key]
|
// expose data on ctx during dev
|
||||||
|
Object.defineProperty(ctx, key, {
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
get: () => data[key],
|
||||||
|
set: NOOP
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
instance.data = reactive(data)
|
instance.data = reactive(data)
|
||||||
@ -345,37 +342,36 @@ export function applyOptions(
|
|||||||
if (computedOptions) {
|
if (computedOptions) {
|
||||||
for (const key in computedOptions) {
|
for (const key in computedOptions) {
|
||||||
const opt = (computedOptions as ComputedOptions)[key]
|
const opt = (computedOptions as ComputedOptions)[key]
|
||||||
if (isFunction(opt)) {
|
const get = isFunction(opt)
|
||||||
renderContext[key] = computed(opt.bind(ctx, ctx))
|
? opt.bind(publicThis, publicThis)
|
||||||
} else {
|
: isFunction(opt.get)
|
||||||
const { get, set } = opt
|
? opt.get.bind(publicThis, publicThis)
|
||||||
if (isFunction(get)) {
|
: NOOP
|
||||||
renderContext[key] = computed({
|
if (__DEV__ && get === NOOP) {
|
||||||
get: get.bind(ctx, ctx),
|
warn(`Computed property "${key}" has no getter.`)
|
||||||
set: isFunction(set)
|
|
||||||
? set.bind(ctx)
|
|
||||||
: __DEV__
|
|
||||||
? () => {
|
|
||||||
warn(
|
|
||||||
`Write operation failed: computed property "${key}" is readonly.`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
: NOOP
|
|
||||||
})
|
|
||||||
} else if (__DEV__) {
|
|
||||||
warn(`Computed property "${key}" has no getter.`)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
const set =
|
||||||
|
!isFunction(opt) && isFunction(opt.set)
|
||||||
|
? opt.set.bind(publicThis)
|
||||||
|
: __DEV__
|
||||||
|
? () => {
|
||||||
|
warn(
|
||||||
|
`Write operation failed: computed property "${key}" is readonly.`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
: NOOP
|
||||||
|
const c = computed({
|
||||||
|
get,
|
||||||
|
set
|
||||||
|
})
|
||||||
|
Object.defineProperty(ctx, key, {
|
||||||
|
enumerable: true,
|
||||||
|
configurable: true,
|
||||||
|
get: () => c.value,
|
||||||
|
set: v => (c.value = v)
|
||||||
|
})
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
checkDuplicateProperties!(OptionTypes.COMPUTED, key)
|
checkDuplicateProperties!(OptionTypes.COMPUTED, key)
|
||||||
if (renderContext[key] && !(key in proxyTarget)) {
|
|
||||||
Object.defineProperty(proxyTarget, key, {
|
|
||||||
enumerable: true,
|
|
||||||
configurable: true,
|
|
||||||
get: () => (renderContext[key] as ComputedRef).value,
|
|
||||||
set: NOOP
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -384,12 +380,9 @@ export function applyOptions(
|
|||||||
for (const key in methods) {
|
for (const key in methods) {
|
||||||
const methodHandler = (methods as MethodOptions)[key]
|
const methodHandler = (methods as MethodOptions)[key]
|
||||||
if (isFunction(methodHandler)) {
|
if (isFunction(methodHandler)) {
|
||||||
renderContext[key] = methodHandler.bind(ctx)
|
ctx[key] = methodHandler.bind(publicThis)
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
checkDuplicateProperties!(OptionTypes.METHODS, key)
|
checkDuplicateProperties!(OptionTypes.METHODS, key)
|
||||||
if (!(key in proxyTarget)) {
|
|
||||||
proxyTarget[key] = renderContext[key]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (__DEV__) {
|
} else if (__DEV__) {
|
||||||
warn(
|
warn(
|
||||||
@ -402,13 +395,13 @@ export function applyOptions(
|
|||||||
|
|
||||||
if (watchOptions) {
|
if (watchOptions) {
|
||||||
for (const key in watchOptions) {
|
for (const key in watchOptions) {
|
||||||
createWatcher(watchOptions[key], renderContext, ctx, key)
|
createWatcher(watchOptions[key], ctx, publicThis, key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (provideOptions) {
|
if (provideOptions) {
|
||||||
const provides = isFunction(provideOptions)
|
const provides = isFunction(provideOptions)
|
||||||
? provideOptions.call(ctx)
|
? provideOptions.call(publicThis)
|
||||||
: provideOptions
|
: provideOptions
|
||||||
for (const key in provides) {
|
for (const key in provides) {
|
||||||
provide(key, provides[key])
|
provide(key, provides[key])
|
||||||
@ -419,23 +412,21 @@ export function applyOptions(
|
|||||||
if (isArray(injectOptions)) {
|
if (isArray(injectOptions)) {
|
||||||
for (let i = 0; i < injectOptions.length; i++) {
|
for (let i = 0; i < injectOptions.length; i++) {
|
||||||
const key = injectOptions[i]
|
const key = injectOptions[i]
|
||||||
renderContext[key] = inject(key)
|
ctx[key] = inject(key)
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
checkDuplicateProperties!(OptionTypes.INJECT, key)
|
checkDuplicateProperties!(OptionTypes.INJECT, key)
|
||||||
proxyTarget[key] = renderContext[key]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (const key in injectOptions) {
|
for (const key in injectOptions) {
|
||||||
const opt = injectOptions[key]
|
const opt = injectOptions[key]
|
||||||
if (isObject(opt)) {
|
if (isObject(opt)) {
|
||||||
renderContext[key] = inject(opt.from, opt.default)
|
ctx[key] = inject(opt.from, opt.default)
|
||||||
} else {
|
} else {
|
||||||
renderContext[key] = inject(opt)
|
ctx[key] = inject(opt)
|
||||||
}
|
}
|
||||||
if (__DEV__) {
|
if (__DEV__) {
|
||||||
checkDuplicateProperties!(OptionTypes.INJECT, key)
|
checkDuplicateProperties!(OptionTypes.INJECT, key)
|
||||||
proxyTarget[key] = renderContext[key]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -451,40 +442,40 @@ export function applyOptions(
|
|||||||
|
|
||||||
// lifecycle options
|
// lifecycle options
|
||||||
if (!asMixin) {
|
if (!asMixin) {
|
||||||
callSyncHook('created', options, ctx, globalMixins)
|
callSyncHook('created', options, publicThis, globalMixins)
|
||||||
}
|
}
|
||||||
if (beforeMount) {
|
if (beforeMount) {
|
||||||
onBeforeMount(beforeMount.bind(ctx))
|
onBeforeMount(beforeMount.bind(publicThis))
|
||||||
}
|
}
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
onMounted(mounted.bind(ctx))
|
onMounted(mounted.bind(publicThis))
|
||||||
}
|
}
|
||||||
if (beforeUpdate) {
|
if (beforeUpdate) {
|
||||||
onBeforeUpdate(beforeUpdate.bind(ctx))
|
onBeforeUpdate(beforeUpdate.bind(publicThis))
|
||||||
}
|
}
|
||||||
if (updated) {
|
if (updated) {
|
||||||
onUpdated(updated.bind(ctx))
|
onUpdated(updated.bind(publicThis))
|
||||||
}
|
}
|
||||||
if (activated) {
|
if (activated) {
|
||||||
onActivated(activated.bind(ctx))
|
onActivated(activated.bind(publicThis))
|
||||||
}
|
}
|
||||||
if (deactivated) {
|
if (deactivated) {
|
||||||
onDeactivated(deactivated.bind(ctx))
|
onDeactivated(deactivated.bind(publicThis))
|
||||||
}
|
}
|
||||||
if (errorCaptured) {
|
if (errorCaptured) {
|
||||||
onErrorCaptured(errorCaptured.bind(ctx))
|
onErrorCaptured(errorCaptured.bind(publicThis))
|
||||||
}
|
}
|
||||||
if (renderTracked) {
|
if (renderTracked) {
|
||||||
onRenderTracked(renderTracked.bind(ctx))
|
onRenderTracked(renderTracked.bind(publicThis))
|
||||||
}
|
}
|
||||||
if (renderTriggered) {
|
if (renderTriggered) {
|
||||||
onRenderTriggered(renderTriggered.bind(ctx))
|
onRenderTriggered(renderTriggered.bind(publicThis))
|
||||||
}
|
}
|
||||||
if (beforeUnmount) {
|
if (beforeUnmount) {
|
||||||
onBeforeUnmount(beforeUnmount.bind(ctx))
|
onBeforeUnmount(beforeUnmount.bind(publicThis))
|
||||||
}
|
}
|
||||||
if (unmounted) {
|
if (unmounted) {
|
||||||
onUnmounted(unmounted.bind(ctx))
|
onUnmounted(unmounted.bind(publicThis))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -533,25 +524,25 @@ function applyMixins(
|
|||||||
|
|
||||||
function createWatcher(
|
function createWatcher(
|
||||||
raw: ComponentWatchOptionItem,
|
raw: ComponentWatchOptionItem,
|
||||||
renderContext: Data,
|
ctx: Data,
|
||||||
ctx: ComponentPublicInstance,
|
publicThis: ComponentPublicInstance,
|
||||||
key: string
|
key: string
|
||||||
) {
|
) {
|
||||||
const getter = () => (ctx as Data)[key]
|
const getter = () => (publicThis as Data)[key]
|
||||||
if (isString(raw)) {
|
if (isString(raw)) {
|
||||||
const handler = renderContext[raw]
|
const handler = ctx[raw]
|
||||||
if (isFunction(handler)) {
|
if (isFunction(handler)) {
|
||||||
watch(getter, handler as WatchCallback)
|
watch(getter, handler as WatchCallback)
|
||||||
} else if (__DEV__) {
|
} else if (__DEV__) {
|
||||||
warn(`Invalid watch handler specified by key "${raw}"`, handler)
|
warn(`Invalid watch handler specified by key "${raw}"`, handler)
|
||||||
}
|
}
|
||||||
} else if (isFunction(raw)) {
|
} else if (isFunction(raw)) {
|
||||||
watch(getter, raw.bind(ctx))
|
watch(getter, raw.bind(publicThis))
|
||||||
} else if (isObject(raw)) {
|
} else if (isObject(raw)) {
|
||||||
if (isArray(raw)) {
|
if (isArray(raw)) {
|
||||||
raw.forEach(r => createWatcher(r, renderContext, ctx, key))
|
raw.forEach(r => createWatcher(r, ctx, publicThis, key))
|
||||||
} else {
|
} else {
|
||||||
watch(getter, raw.handler.bind(ctx), raw)
|
watch(getter, raw.handler.bind(publicThis), raw)
|
||||||
}
|
}
|
||||||
} else if (__DEV__) {
|
} else if (__DEV__) {
|
||||||
warn(`Invalid watch option: "${key}"`)
|
warn(`Invalid watch option: "${key}"`)
|
||||||
|
@ -83,25 +83,24 @@ const enum AccessTypes {
|
|||||||
OTHER
|
OTHER
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ComponentPublicProxyTarget {
|
export interface ComponentRenderContext {
|
||||||
[key: string]: any
|
[key: string]: any
|
||||||
_: ComponentInternalInstance
|
_: ComponentInternalInstance
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
|
export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
|
||||||
get({ _: instance }: ComponentPublicProxyTarget, key: string) {
|
get({ _: instance }: ComponentRenderContext, key: string) {
|
||||||
const {
|
const {
|
||||||
renderContext,
|
ctx,
|
||||||
setupState,
|
setupState,
|
||||||
data,
|
data,
|
||||||
props,
|
props,
|
||||||
accessCache,
|
accessCache,
|
||||||
type,
|
type,
|
||||||
proxyTarget,
|
|
||||||
appContext
|
appContext
|
||||||
} = instance
|
} = instance
|
||||||
|
|
||||||
// data / props / renderContext
|
// data / props / ctx
|
||||||
// This getter gets called for every property access on the render context
|
// 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
|
// 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
|
// 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:
|
case AccessTypes.DATA:
|
||||||
return data[key]
|
return data[key]
|
||||||
case AccessTypes.CONTEXT:
|
case AccessTypes.CONTEXT:
|
||||||
return renderContext[key]
|
return ctx[key]
|
||||||
case AccessTypes.PROPS:
|
case AccessTypes.PROPS:
|
||||||
return props![key]
|
return props![key]
|
||||||
// default: just fallthrough
|
// default: just fallthrough
|
||||||
@ -135,31 +134,30 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
|
|||||||
) {
|
) {
|
||||||
accessCache![key] = AccessTypes.PROPS
|
accessCache![key] = AccessTypes.PROPS
|
||||||
return props![key]
|
return props![key]
|
||||||
} else if (renderContext !== EMPTY_OBJ && hasOwn(renderContext, key)) {
|
} else if (ctx !== EMPTY_OBJ && hasOwn(ctx, key)) {
|
||||||
accessCache![key] = AccessTypes.CONTEXT
|
accessCache![key] = AccessTypes.CONTEXT
|
||||||
return renderContext[key]
|
return ctx[key]
|
||||||
} else {
|
} else {
|
||||||
accessCache![key] = AccessTypes.OTHER
|
accessCache![key] = AccessTypes.OTHER
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// public $xxx properties &
|
|
||||||
// user-attached properties (falls through to proxyTarget)
|
|
||||||
const publicGetter = publicPropertiesMap[key]
|
const publicGetter = publicPropertiesMap[key]
|
||||||
let cssModule, globalProperties
|
let cssModule, globalProperties
|
||||||
|
// public $xxx properties
|
||||||
if (publicGetter) {
|
if (publicGetter) {
|
||||||
if (__DEV__ && key === '$attrs') {
|
if (__DEV__ && key === '$attrs') {
|
||||||
markAttrsAccessed()
|
markAttrsAccessed()
|
||||||
}
|
}
|
||||||
return publicGetter(instance)
|
return publicGetter(instance)
|
||||||
} else if (hasOwn(proxyTarget, key)) {
|
|
||||||
return proxyTarget[key]
|
|
||||||
} else if (
|
} else if (
|
||||||
|
// css module (injected by vue-loader)
|
||||||
(cssModule = type.__cssModules) &&
|
(cssModule = type.__cssModules) &&
|
||||||
(cssModule = cssModule[key])
|
(cssModule = cssModule[key])
|
||||||
) {
|
) {
|
||||||
return cssModule
|
return cssModule
|
||||||
} else if (
|
} else if (
|
||||||
|
// global properties
|
||||||
((globalProperties = appContext.config.globalProperties),
|
((globalProperties = appContext.config.globalProperties),
|
||||||
hasOwn(globalProperties, key))
|
hasOwn(globalProperties, key))
|
||||||
) {
|
) {
|
||||||
@ -173,11 +171,11 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
set(
|
set(
|
||||||
{ _: instance }: ComponentPublicProxyTarget,
|
{ _: instance }: ComponentRenderContext,
|
||||||
key: string,
|
key: string,
|
||||||
value: any
|
value: any
|
||||||
): boolean {
|
): boolean {
|
||||||
const { data, setupState, renderContext } = instance
|
const { data, setupState, ctx } = instance
|
||||||
if (setupState !== EMPTY_OBJ && hasOwn(setupState, key)) {
|
if (setupState !== EMPTY_OBJ && hasOwn(setupState, key)) {
|
||||||
setupState[key] = value
|
setupState[key] = value
|
||||||
} else if (data !== EMPTY_OBJ && hasOwn(data, key)) {
|
} else if (data !== EMPTY_OBJ && hasOwn(data, key)) {
|
||||||
@ -189,9 +187,8 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
|
|||||||
instance
|
instance
|
||||||
)
|
)
|
||||||
return false
|
return false
|
||||||
} else if (hasOwn(renderContext, key)) {
|
}
|
||||||
renderContext[key] = value
|
if (key[0] === '$' && key.slice(1) in instance) {
|
||||||
} else if (key[0] === '$' && key.slice(1) in instance) {
|
|
||||||
__DEV__ &&
|
__DEV__ &&
|
||||||
warn(
|
warn(
|
||||||
`Attempting to mutate public property "${key}". ` +
|
`Attempting to mutate public property "${key}". ` +
|
||||||
@ -201,13 +198,13 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
|
|||||||
return false
|
return false
|
||||||
} else {
|
} else {
|
||||||
if (__DEV__ && key in instance.appContext.config.globalProperties) {
|
if (__DEV__ && key in instance.appContext.config.globalProperties) {
|
||||||
Object.defineProperty(instance.proxyTarget, key, {
|
Object.defineProperty(ctx, key, {
|
||||||
configurable: true,
|
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
|
configurable: true,
|
||||||
value
|
value
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
instance.proxyTarget[key] = value
|
ctx[key] = value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@ -215,16 +212,8 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
|
|||||||
|
|
||||||
has(
|
has(
|
||||||
{
|
{
|
||||||
_: {
|
_: { data, setupState, accessCache, ctx, type, appContext }
|
||||||
data,
|
}: ComponentRenderContext,
|
||||||
setupState,
|
|
||||||
accessCache,
|
|
||||||
renderContext,
|
|
||||||
type,
|
|
||||||
proxyTarget,
|
|
||||||
appContext
|
|
||||||
}
|
|
||||||
}: ComponentPublicProxyTarget,
|
|
||||||
key: string
|
key: string
|
||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
@ -232,18 +221,15 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
|
|||||||
(data !== EMPTY_OBJ && hasOwn(data, key)) ||
|
(data !== EMPTY_OBJ && hasOwn(data, key)) ||
|
||||||
(setupState !== EMPTY_OBJ && hasOwn(setupState, key)) ||
|
(setupState !== EMPTY_OBJ && hasOwn(setupState, key)) ||
|
||||||
(type.props && hasOwn(normalizePropsOptions(type.props)[0]!, key)) ||
|
(type.props && hasOwn(normalizePropsOptions(type.props)[0]!, key)) ||
|
||||||
hasOwn(renderContext, key) ||
|
hasOwn(ctx, key) ||
|
||||||
hasOwn(publicPropertiesMap, key) ||
|
hasOwn(publicPropertiesMap, key) ||
|
||||||
hasOwn(proxyTarget, key) ||
|
|
||||||
hasOwn(appContext.config.globalProperties, key)
|
hasOwn(appContext.config.globalProperties, key)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (__DEV__ && !__TEST__) {
|
if (__DEV__ && !__TEST__) {
|
||||||
PublicInstanceProxyHandlers.ownKeys = (
|
PublicInstanceProxyHandlers.ownKeys = (target: ComponentRenderContext) => {
|
||||||
target: ComponentPublicProxyTarget
|
|
||||||
) => {
|
|
||||||
warn(
|
warn(
|
||||||
`Avoid app logic that relies on enumerating keys on a component instance. ` +
|
`Avoid app logic that relies on enumerating keys on a component instance. ` +
|
||||||
`The keys will be empty in production mode to avoid performance overhead.`
|
`The keys will be empty in production mode to avoid performance overhead.`
|
||||||
@ -254,14 +240,14 @@ if (__DEV__ && !__TEST__) {
|
|||||||
|
|
||||||
export const RuntimeCompiledPublicInstanceProxyHandlers = {
|
export const RuntimeCompiledPublicInstanceProxyHandlers = {
|
||||||
...PublicInstanceProxyHandlers,
|
...PublicInstanceProxyHandlers,
|
||||||
get(target: ComponentPublicProxyTarget, key: string) {
|
get(target: ComponentRenderContext, key: string) {
|
||||||
// fast path for unscopables when using `with` block
|
// fast path for unscopables when using `with` block
|
||||||
if ((key as any) === Symbol.unscopables) {
|
if ((key as any) === Symbol.unscopables) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
return PublicInstanceProxyHandlers.get!(target, key, target)
|
return PublicInstanceProxyHandlers.get!(target, key, target)
|
||||||
},
|
},
|
||||||
has(_: ComponentPublicProxyTarget, key: string) {
|
has(_: ComponentRenderContext, key: string) {
|
||||||
return key[0] !== '_' && !isGloballyWhitelisted(key)
|
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`
|
// 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
|
// for easier console inspection. In prod mode it will be an empty object so
|
||||||
// these properties definitions can be skipped.
|
// these properties definitions can be skipped.
|
||||||
export function createDevProxyTarget(instance: ComponentInternalInstance) {
|
export function createRenderContext(instance: ComponentInternalInstance) {
|
||||||
const target: Record<string, any> = {}
|
const target: Record<string, any> = {}
|
||||||
|
|
||||||
// expose internal instance for proxy handlers
|
// 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
|
instance: ComponentInternalInstance
|
||||||
) {
|
) {
|
||||||
const {
|
const {
|
||||||
proxyTarget,
|
ctx,
|
||||||
type: { props: propsOptions }
|
type: { props: propsOptions }
|
||||||
} = instance
|
} = instance
|
||||||
if (propsOptions) {
|
if (propsOptions) {
|
||||||
Object.keys(normalizePropsOptions(propsOptions)[0]!).forEach(key => {
|
Object.keys(normalizePropsOptions(propsOptions)[0]!).forEach(key => {
|
||||||
Object.defineProperty(proxyTarget, key, {
|
Object.defineProperty(ctx, key, {
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
configurable: true,
|
configurable: true,
|
||||||
get: () => instance.props[key],
|
get: () => instance.props[key],
|
||||||
@ -324,12 +311,13 @@ export function exposePropsOnDevProxyTarget(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function exposeSetupStateOnDevProxyTarget(
|
// dev only
|
||||||
|
export function exposeSetupStateOnRenderContext(
|
||||||
instance: ComponentInternalInstance
|
instance: ComponentInternalInstance
|
||||||
) {
|
) {
|
||||||
const { proxyTarget, setupState } = instance
|
const { ctx, setupState } = instance
|
||||||
Object.keys(toRaw(setupState)).forEach(key => {
|
Object.keys(toRaw(setupState)).forEach(key => {
|
||||||
Object.defineProperty(proxyTarget, key, {
|
Object.defineProperty(ctx, key, {
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
configurable: true,
|
configurable: true,
|
||||||
get: () => setupState[key],
|
get: () => setupState[key],
|
||||||
|
@ -26,7 +26,7 @@ import {
|
|||||||
RendererNode
|
RendererNode
|
||||||
} from '../renderer'
|
} from '../renderer'
|
||||||
import { setTransitionHooks } from './BaseTransition'
|
import { setTransitionHooks } from './BaseTransition'
|
||||||
import { ComponentPublicProxyTarget } from '../componentProxy'
|
import { ComponentRenderContext } from '../componentProxy'
|
||||||
|
|
||||||
type MatchPattern = string | RegExp | string[] | RegExp[]
|
type MatchPattern = string | RegExp | string[] | RegExp[]
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ type CacheKey = string | number | Component
|
|||||||
type Cache = Map<CacheKey, VNode>
|
type Cache = Map<CacheKey, VNode>
|
||||||
type Keys = Set<CacheKey>
|
type Keys = Set<CacheKey>
|
||||||
|
|
||||||
export interface KeepAliveContext extends ComponentPublicProxyTarget {
|
export interface KeepAliveContext extends ComponentRenderContext {
|
||||||
renderer: RendererInternals
|
renderer: RendererInternals
|
||||||
activate: (
|
activate: (
|
||||||
vnode: VNode,
|
vnode: VNode,
|
||||||
@ -77,12 +77,12 @@ const KeepAliveImpl = {
|
|||||||
const instance = getCurrentInstance()!
|
const instance = getCurrentInstance()!
|
||||||
const parentSuspense = instance.suspense
|
const parentSuspense = instance.suspense
|
||||||
|
|
||||||
// KeepAlive communicates with the instantiated renderer via the proxyTarget
|
// KeepAlive communicates with the instantiated renderer via the
|
||||||
// as a shared context where the renderer passes in its internals,
|
// ctx where the renderer passes in its internals,
|
||||||
// and the KeepAlive instance exposes activate/deactivate implementations.
|
// and the KeepAlive instance exposes activate/deactivate implementations.
|
||||||
// The whole point of this is to avoid importing KeepAlive directly in the
|
// The whole point of this is to avoid importing KeepAlive directly in the
|
||||||
// renderer to facilitate tree-shaking.
|
// renderer to facilitate tree-shaking.
|
||||||
const sharedContext = instance.proxyTarget as KeepAliveContext
|
const sharedContext = instance.ctx as KeepAliveContext
|
||||||
const {
|
const {
|
||||||
renderer: {
|
renderer: {
|
||||||
p: patch,
|
p: patch,
|
||||||
|
@ -949,7 +949,7 @@ function baseCreateRenderer(
|
|||||||
) => {
|
) => {
|
||||||
if (n1 == null) {
|
if (n1 == null) {
|
||||||
if (n2.shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE) {
|
if (n2.shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE) {
|
||||||
;(parentComponent!.proxyTarget as KeepAliveContext).activate(
|
;(parentComponent!.ctx as KeepAliveContext).activate(
|
||||||
n2,
|
n2,
|
||||||
container,
|
container,
|
||||||
anchor,
|
anchor,
|
||||||
@ -998,7 +998,7 @@ function baseCreateRenderer(
|
|||||||
|
|
||||||
// inject renderer internals for keepAlive
|
// inject renderer internals for keepAlive
|
||||||
if (isKeepAlive(initialVNode)) {
|
if (isKeepAlive(initialVNode)) {
|
||||||
;(instance.proxyTarget as KeepAliveContext).renderer = internals
|
;(instance.ctx as KeepAliveContext).renderer = internals
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolve props and slots for setup context
|
// resolve props and slots for setup context
|
||||||
@ -1719,7 +1719,7 @@ function baseCreateRenderer(
|
|||||||
|
|
||||||
if (shapeFlag & ShapeFlags.COMPONENT) {
|
if (shapeFlag & ShapeFlags.COMPONENT) {
|
||||||
if (shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) {
|
if (shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) {
|
||||||
;(parentComponent!.proxyTarget as KeepAliveContext).deactivate(vnode)
|
;(parentComponent!.ctx as KeepAliveContext).deactivate(vnode)
|
||||||
} else {
|
} else {
|
||||||
unmountComponent(vnode.component!, parentSuspense, doRemove)
|
unmountComponent(vnode.component!, parentSuspense, doRemove)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user