vue3-yuanma/packages/runtime-core/src/componentProxy.ts

109 lines
3.1 KiB
TypeScript
Raw Normal View History

2019-09-07 00:58:31 +08:00
import { ComponentInternalInstance, Data } from './component'
2019-09-04 11:04:11 +08:00
import { nextTick } from './scheduler'
2019-09-04 23:36:27 +08:00
import { instanceWatch } from './apiWatch'
2019-09-06 08:48:14 +08:00
import { EMPTY_OBJ, hasOwn } from '@vue/shared'
import { ExtracComputedReturns } from './apiOptions'
import { UnwrapRef } from '@vue/reactivity'
// public properties exposed on the proxy, which is used as the render context
// in templates (as `this` in the render option)
2019-09-07 00:58:31 +08:00
export type ComponentPublicInstance<
P = {},
B = {},
D = {},
C = {},
M = {},
PublicProps = P
> = {
$data: D
$props: PublicProps
$attrs: Data
$refs: Data
$slots: Data
2019-09-07 00:58:31 +08:00
$root: ComponentInternalInstance | null
$parent: ComponentInternalInstance | null
$emit: (event: string, ...args: unknown[]) => void
} & P &
UnwrapRef<B> &
D &
ExtracComputedReturns<C> &
M
2019-05-29 13:43:46 +08:00
2019-09-07 00:58:31 +08:00
export const PublicInstanceProxyHandlers = {
get(target: ComponentInternalInstance, key: string) {
const { renderContext, data, props, propsProxy } = target
2019-09-06 08:48:14 +08:00
if (data !== EMPTY_OBJ && hasOwn(data, key)) {
2019-06-19 17:08:42 +08:00
return data[key]
2019-09-06 08:48:14 +08:00
} else if (hasOwn(renderContext, key)) {
return renderContext[key]
2019-09-06 08:48:14 +08:00
} else if (hasOwn(props, key)) {
// return the value from propsProxy for ref unwrapping and readonly
return (propsProxy as any)[key]
2019-05-29 13:43:46 +08:00
} else {
2019-09-23 04:50:57 +08:00
// TODO simplify this?
2019-05-29 13:43:46 +08:00
switch (key) {
2019-06-19 17:08:42 +08:00
case '$data':
return data
2019-05-29 13:43:46 +08:00
case '$props':
return propsProxy
2019-05-29 13:43:46 +08:00
case '$attrs':
return target.attrs
case '$slots':
return target.slots
case '$refs':
return target.refs
2019-06-03 13:44:45 +08:00
case '$parent':
return target.parent
case '$root':
return target.root
2019-06-19 16:43:34 +08:00
case '$emit':
return target.emit
2019-09-04 11:04:11 +08:00
case '$el':
return target.vnode.el
case '$options':
return target.type
2019-05-29 13:43:46 +08:00
default:
2019-09-04 11:04:11 +08:00
// methods are only exposed when options are supported
if (__FEATURE_OPTIONS__) {
switch (key) {
case '$forceUpdate':
return target.update
case '$nextTick':
return nextTick
case '$watch':
2019-09-04 23:36:27 +08:00
return instanceWatch.bind(target)
2019-09-04 11:04:11 +08:00
}
}
2019-08-21 21:50:20 +08:00
return target.user[key]
2019-05-29 13:43:46 +08:00
}
}
},
2019-09-20 12:12:37 +08:00
has(target: ComponentInternalInstance, key: string): boolean {
const { renderContext, data, props } = target
2019-09-23 04:50:57 +08:00
// TODO handle $xxx properties
2019-09-20 12:12:37 +08:00
return (
key[0] !== '_' &&
((data !== EMPTY_OBJ && hasOwn(data, key)) ||
hasOwn(renderContext, key) ||
hasOwn(props, key))
2019-09-20 12:12:37 +08:00
)
},
2019-09-07 00:58:31 +08:00
set(target: ComponentInternalInstance, key: string, value: any): boolean {
const { data, renderContext } = target
2019-09-06 08:48:14 +08:00
if (data !== EMPTY_OBJ && hasOwn(data, key)) {
2019-06-19 17:08:42 +08:00
data[key] = value
2019-09-06 08:48:14 +08:00
} else if (hasOwn(renderContext, key)) {
renderContext[key] = value
2019-08-21 21:50:20 +08:00
} else if (key[0] === '$' && key.slice(1) in target) {
// TODO warn attempt of mutating public property
return false
} else if (key in target.props) {
// TODO warn attempt of mutating prop
return false
2019-05-29 13:43:46 +08:00
} else {
2019-08-21 21:50:20 +08:00
target.user[key] = value
2019-05-29 13:43:46 +08:00
}
return true
2019-05-29 13:43:46 +08:00
}
}