| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181 |
- import { ComponentInternalInstance, Data, Emit } from './component'
- import { nextTick } from './scheduler'
- import { instanceWatch } from './apiWatch'
- import { EMPTY_OBJ, hasOwn, isGloballyWhitelisted } from '@vue/shared'
- import {
- ExtractComputedReturns,
- ComponentOptionsBase,
- ComputedOptions,
- MethodOptions
- } from './apiOptions'
- import { UnwrapRef, ReactiveEffect } from '@vue/reactivity'
- import { warn } from './warning'
- import { Slots } from './componentSlots'
- import {
- currentRenderingInstance,
- markAttrsAccessed
- } from './componentRenderUtils'
- // public properties exposed on the proxy, which is used as the render context
- // in templates (as `this` in the render option)
- export type ComponentPublicInstance<
- P = {},
- B = {},
- D = {},
- C extends ComputedOptions = {},
- M extends MethodOptions = {},
- PublicProps = P
- > = {
- $data: D
- $props: PublicProps
- $attrs: Data
- $refs: Data
- $slots: Slots
- $root: ComponentInternalInstance | null
- $parent: ComponentInternalInstance | null
- $emit: Emit
- $el: any
- $options: ComponentOptionsBase<P, B, D, C, M>
- $forceUpdate: ReactiveEffect
- $nextTick: typeof nextTick
- $watch: typeof instanceWatch
- } & P &
- UnwrapRef<B> &
- D &
- ExtractComputedReturns<C> &
- M
- const publicPropertiesMap: Record<
- string,
- (i: ComponentInternalInstance) => any
- > = {
- $: i => i,
- $el: i => i.vnode.el,
- $cache: i => i.renderCache,
- $data: i => i.data,
- $props: i => i.propsProxy,
- $attrs: i => i.attrs,
- $slots: i => i.slots,
- $refs: i => i.refs,
- $parent: i => i.parent,
- $root: i => i.root,
- $emit: i => i.emit,
- $options: i => i.type,
- $forceUpdate: i => i.update,
- $nextTick: () => nextTick,
- $watch: i => instanceWatch.bind(i)
- }
- const enum AccessTypes {
- DATA,
- CONTEXT,
- PROPS
- }
- export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
- get(target: ComponentInternalInstance, key: string) {
- // fast path for unscopables when using `with` block
- if (__RUNTIME_COMPILE__ && (key as any) === Symbol.unscopables) {
- return
- }
- const {
- renderContext,
- data,
- props,
- propsProxy,
- accessCache,
- type,
- sink
- } = target
- // data / props / renderContext
- // 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
- // access on a plain object, so we use an accessCache object (with null
- // prototype) to memoize what access type a key corresponds to.
- const n = accessCache![key]
- if (n !== undefined) {
- switch (n) {
- case AccessTypes.DATA:
- return data[key]
- case AccessTypes.CONTEXT:
- return renderContext[key]
- case AccessTypes.PROPS:
- return propsProxy![key]
- }
- } else if (data !== EMPTY_OBJ && hasOwn(data, key)) {
- accessCache![key] = AccessTypes.DATA
- return data[key]
- } else if (hasOwn(renderContext, key)) {
- accessCache![key] = AccessTypes.CONTEXT
- return renderContext[key]
- } else if (hasOwn(props, key)) {
- // only cache props access if component has declared (thus stable) props
- if (type.props != null) {
- accessCache![key] = AccessTypes.PROPS
- }
- // return the value from propsProxy for ref unwrapping and readonly
- return propsProxy![key]
- }
- // public $xxx properties & user-attached properties (sink)
- const publicGetter = publicPropertiesMap[key]
- if (publicGetter !== undefined) {
- if (__DEV__ && key === '$attrs') {
- markAttrsAccessed()
- }
- return publicGetter(target)
- } else if (hasOwn(sink, key)) {
- return sink[key]
- } else if (__DEV__ && currentRenderingInstance != null) {
- warn(
- `Property ${JSON.stringify(key)} was accessed during render ` +
- `but is not defined on instance.`
- )
- }
- },
- has(target: ComponentInternalInstance, key: string) {
- const { data, accessCache, renderContext, type, sink } = target
- return (
- accessCache![key] !== undefined ||
- (data !== EMPTY_OBJ && hasOwn(data, key)) ||
- hasOwn(renderContext, key) ||
- (type.props != null && hasOwn(type.props, key)) ||
- hasOwn(publicPropertiesMap, key) ||
- hasOwn(sink, key)
- )
- },
- set(target: ComponentInternalInstance, key: string, value: any): boolean {
- const { data, renderContext } = target
- if (data !== EMPTY_OBJ && hasOwn(data, key)) {
- data[key] = value
- } else if (hasOwn(renderContext, key)) {
- renderContext[key] = value
- } else if (key[0] === '$' && key.slice(1) in target) {
- __DEV__ &&
- warn(
- `Attempting to mutate public property "${key}". ` +
- `Properties starting with $ are reserved and readonly.`,
- target
- )
- return false
- } else if (key in target.props) {
- __DEV__ &&
- warn(`Attempting to mutate prop "${key}". Props are readonly.`, target)
- return false
- } else {
- target.sink[key] = value
- }
- return true
- }
- }
- export const runtimeCompiledRenderProxyHandlers = {
- ...PublicInstanceProxyHandlers,
- has(_target: ComponentInternalInstance, key: string) {
- return key[0] !== '_' && !isGloballyWhitelisted(key)
- }
- }
|