expect(`Attempting to mutate public property "$data"`).toHaveBeenWarned()
})
- test('sink', async () => {
+ test('user attached properties', async () => {
let instance: ComponentInternalInstance
let instanceProxy: any
const Comp = {
render(h(Comp), nodeOps.createElement('div'))
instanceProxy.foo = 1
expect(instanceProxy.foo).toBe(1)
- expect(instance!.sink.foo).toBe(1)
+ expect(instance!.proxyTarget.foo).toBe(1)
})
test('globalProperties', () => {
// set should overwrite globalProperties with local
instanceProxy.foo = 2
- expect(instanceProxy.foo).toBe(2)
- expect(instance!.sink.foo).toBe(2)
+ // expect(instanceProxy.foo).toBe(2)
+ expect(instance!.proxyTarget.foo).toBe(2)
// should not affect global
expect(app.config.globalProperties.foo).toBe(1)
})
expect('$foobar' in instanceProxy).toBe(false)
expect('baz' in instanceProxy).toBe(false)
- // set non-existent (goes into sink)
+ // set non-existent (goes into proxyTarget sink)
instanceProxy.baz = 1
expect('baz' in instanceProxy).toBe(true)
attrs: Data
slots: InternalSlots
proxy: ComponentPublicInstance | 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
asyncDep: Promise<any> | null
asyncResolved: boolean
- // storage for any extra properties
- sink: { [key: string]: any }
-
// lifecycle
isMounted: boolean
isUnmounted: boolean
asyncDep: null,
asyncResolved: false,
- // user namespace for storing whatever the user assigns to `this`
- // can also be used as a wildcard storage for ad-hoc injections internally
- sink: {},
-
// lifecycle hooks
// not using enums here because it results in computed properties
isMounted: false,
props,
accessCache,
type,
- sink,
+ proxyTarget,
appContext
} = instance
}
}
- // public $xxx properties & user-attached properties (sink)
+ // public $xxx properties &
+ // user-attached properties (falls through to proxyTarget)
const publicGetter = publicPropertiesMap[key]
let cssModule, globalProperties
if (publicGetter) {
markAttrsAccessed()
}
return publicGetter(instance)
- } else if (hasOwn(sink, key)) {
- return sink[key]
+ } else if (hasOwn(proxyTarget, key)) {
+ return proxyTarget[key]
} else if (
(cssModule = type.__cssModules) &&
(cssModule = cssModule[key])
)
return false
} else {
- instance.sink[key] = value
- if (__DEV__) {
+ if (__DEV__ && key in instance.appContext.config.globalProperties) {
+ Object.defineProperty(instance.proxyTarget, key, {
+ configurable: true,
+ enumerable: true,
+ value
+ })
+ } else {
instance.proxyTarget[key] = value
}
}
has(
{
- _: { data, accessCache, renderContext, type, sink, appContext }
+ _: { data, accessCache, renderContext, type, proxyTarget, appContext }
}: ComponentPublicProxyTarget,
key: string
) {
hasOwn(renderContext, key) ||
(type.props && hasOwn(normalizePropsOptions(type.props)[0]!, key)) ||
hasOwn(publicPropertiesMap, key) ||
- hasOwn(sink, key) ||
+ hasOwn(proxyTarget, key) ||
hasOwn(appContext.config.globalProperties, key)
)
}
invokeArrayFns
} from '@vue/shared'
import { watch } from '../apiWatch'
-import { SuspenseBoundary } from './Suspense'
import {
RendererInternals,
queuePostRenderEffect,
RendererNode
} from '../renderer'
import { setTransitionHooks } from './BaseTransition'
+import { ComponentPublicProxyTarget } from '../componentProxy'
type MatchPattern = string | RegExp | string[] | RegExp[]
type Cache = Map<CacheKey, VNode>
type Keys = Set<CacheKey>
-export interface KeepAliveSink {
+export interface KeepAliveContext extends ComponentPublicProxyTarget {
renderer: RendererInternals
- parentSuspense: SuspenseBoundary | null
activate: (
vnode: VNode,
container: RendererElement,
let current: VNode | null = null
const instance = getCurrentInstance()!
+ const parentSuspense = instance.suspense
- // KeepAlive communicates with the instantiated renderer via the "sink"
- // where the renderer passes in platform-specific functions, and the
- // KeepAlive instance exposes activate/deactivate implementations.
+ // KeepAlive communicates with the instantiated renderer via the proxyTarget
+ // as a shared context where the renderer passes in its internals,
+ // and the KeepAlive instance exposes activate/deactivate implementations.
// The whole point of this is to avoid importing KeepAlive directly in the
// renderer to facilitate tree-shaking.
- const sink = instance.sink as KeepAliveSink
+ const sharedContext = instance.proxyTarget as KeepAliveContext
const {
renderer: {
p: patch,
m: move,
um: _unmount,
o: { createElement }
- },
- parentSuspense
- } = sink
+ }
+ } = sharedContext
const storageContainer = createElement('div')
- sink.activate = (vnode, container, anchor, isSVG, optimized) => {
+ sharedContext.activate = (vnode, container, anchor, isSVG, optimized) => {
const child = vnode.component!
move(vnode, container, anchor, MoveType.ENTER, parentSuspense)
// in case props have changed
}, parentSuspense)
}
- sink.deactivate = (vnode: VNode) => {
+ sharedContext.deactivate = (vnode: VNode) => {
move(vnode, storageContainer, null, MoveType.LEAVE, parentSuspense)
queuePostRenderEffect(() => {
const component = vnode.component!
SuspenseImpl
} from './components/Suspense'
import { TeleportImpl } from './components/Teleport'
-import { KeepAliveSink, isKeepAlive } from './components/KeepAlive'
+import { isKeepAlive, KeepAliveContext } from './components/KeepAlive'
import { registerHMR, unregisterHMR } from './hmr'
import {
ErrorCodes,
) => {
if (n1 == null) {
if (n2.shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE) {
- ;(parentComponent!.sink as KeepAliveSink).activate(
+ ;(parentComponent!.proxyTarget as KeepAliveContext).activate(
n2,
container,
anchor,
// inject renderer internals for keepAlive
if (isKeepAlive(initialVNode)) {
- const sink = instance.sink as KeepAliveSink
- sink.renderer = internals
- sink.parentSuspense = parentSuspense
+ ;(instance.proxyTarget as KeepAliveContext).renderer = internals
}
// resolve props and slots for setup context
if (shapeFlag & ShapeFlags.COMPONENT) {
if (shapeFlag & ShapeFlags.COMPONENT_SHOULD_KEEP_ALIVE) {
- ;(parentComponent!.sink as KeepAliveSink).deactivate(vnode)
+ ;(parentComponent!.proxyTarget as KeepAliveContext).deactivate(vnode)
} else {
unmountComponent(vnode.component!, parentSuspense, doRemove)
}