wip: $data should only expose properties from data()

This commit is contained in:
Evan You 2019-09-05 20:36:35 -04:00
parent 94a05561f8
commit 7eed0df3c2
4 changed files with 43 additions and 27 deletions

View File

@ -112,8 +112,10 @@ export function applyOptions(
options: ComponentOptions, options: ComponentOptions,
asMixin: boolean = false asMixin: boolean = false
) { ) {
const data = const renderContext =
instance.data === EMPTY_OBJ ? (instance.data = reactive({})) : instance.data instance.renderContext === EMPTY_OBJ
? (instance.renderContext = reactive({}))
: instance.renderContext
const ctx = instance.renderProxy as any const ctx = instance.renderProxy as any
const { const {
// composition // composition
@ -166,12 +168,16 @@ export function applyOptions(
// state options // state options
if (dataOptions) { if (dataOptions) {
const data =
instance.data === EMPTY_OBJ
? (instance.data = reactive({}))
: instance.data
extend(data, isFunction(dataOptions) ? dataOptions.call(ctx) : dataOptions) extend(data, isFunction(dataOptions) ? dataOptions.call(ctx) : dataOptions)
} }
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]
data[key] = isFunction(opt) renderContext[key] = isFunction(opt)
? computed(opt.bind(ctx)) ? computed(opt.bind(ctx))
: computed({ : computed({
get: opt.get.bind(ctx), get: opt.get.bind(ctx),
@ -181,7 +187,7 @@ export function applyOptions(
} }
if (methods) { if (methods) {
for (const key in methods) { for (const key in methods) {
data[key] = (methods as MethodOptions)[key].bind(ctx) renderContext[key] = (methods as MethodOptions)[key].bind(ctx)
} }
} }
if (watchOptions) { if (watchOptions) {
@ -189,7 +195,7 @@ export function applyOptions(
const raw = watchOptions[key] const raw = watchOptions[key]
const getter = () => ctx[key] const getter = () => ctx[key]
if (isString(raw)) { if (isString(raw)) {
const handler = data[raw] const handler = renderContext[raw]
if (isFunction(handler)) { if (isFunction(handler)) {
watch(getter, handler as any) watch(getter, handler as any)
} else if (__DEV__) { } else if (__DEV__) {
@ -217,15 +223,15 @@ 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]
data[key] = inject(key) renderContext[key] = inject(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)) {
data[key] = inject(opt.from, opt.default) renderContext[key] = inject(opt.from, opt.default)
} else { } else {
data[key] = inject(opt) renderContext[key] = inject(opt)
} }
} }
} }

View File

@ -144,15 +144,17 @@ export const enum LifecycleHooks {
ERROR_CAPTURED = 'ec' ERROR_CAPTURED = 'ec'
} }
type Emit = ((event: string, ...args: unknown[]) => void)
interface SetupContext { interface SetupContext {
attrs: Data attrs: Data
slots: Slots slots: Slots
emit: ((event: string, ...args: unknown[]) => void) emit: Emit
} }
type RenderFunction = () => VNodeChild type RenderFunction = () => VNodeChild
export type ComponentInstance<P = Data, D = Data> = { export interface ComponentInstance {
type: FunctionalComponent | ComponentOptions type: FunctionalComponent | ComponentOptions
parent: ComponentInstance | null parent: ComponentInstance | null
appContext: AppContext appContext: AppContext
@ -169,12 +171,16 @@ export type ComponentInstance<P = Data, D = Data> = {
directives: Record<string, Directive> directives: Record<string, Directive>
// the rest are only for stateful components // the rest are only for stateful components
data: D renderContext: Data
props: P data: Data
props: Data
attrs: Data
slots: Slots
renderProxy: ComponentRenderProxy | null renderProxy: ComponentRenderProxy | null
propsProxy: P | null propsProxy: Data | null
setupContext: SetupContext | null setupContext: SetupContext | null
refs: Data refs: Data
emit: Emit
// user namespace // user namespace
user: { [key: string]: any } user: { [key: string]: any }
@ -193,7 +199,7 @@ export type ComponentInstance<P = Data, D = Data> = {
[LifecycleHooks.ACTIVATED]: LifecycleHook [LifecycleHooks.ACTIVATED]: LifecycleHook
[LifecycleHooks.DEACTIVATED]: LifecycleHook [LifecycleHooks.DEACTIVATED]: LifecycleHook
[LifecycleHooks.ERROR_CAPTURED]: LifecycleHook [LifecycleHooks.ERROR_CAPTURED]: LifecycleHook
} & SetupContext }
// createComponent // createComponent
// overload 1: direct setup function // overload 1: direct setup function
@ -287,6 +293,7 @@ export function createComponentInstance(
provides: parent ? parent.provides : Object.create(appContext.provides), provides: parent ? parent.provides : Object.create(appContext.provides),
// setup context properties // setup context properties
renderContext: EMPTY_OBJ,
data: EMPTY_OBJ, data: EMPTY_OBJ,
props: EMPTY_OBJ, props: EMPTY_OBJ,
attrs: EMPTY_OBJ, attrs: EMPTY_OBJ,
@ -397,7 +404,7 @@ export function setupStatefulComponent(instance: ComponentInstance) {
// setup returned bindings. // setup returned bindings.
// assuming a render function compiled from template is present. // assuming a render function compiled from template is present.
if (isObject(setupResult)) { if (isObject(setupResult)) {
instance.data = reactive(setupResult) instance.renderContext = reactive(setupResult)
} else if (__DEV__ && setupResult !== undefined) { } else if (__DEV__ && setupResult !== undefined) {
warn( warn(
`setup() should return an object. Received: ${ `setup() should return an object. Received: ${
@ -420,8 +427,8 @@ export function setupStatefulComponent(instance: ComponentInstance) {
if (__FEATURE_OPTIONS__) { if (__FEATURE_OPTIONS__) {
applyOptions(instance, Component) applyOptions(instance, Component)
} }
if (instance.data === EMPTY_OBJ) { if (instance.renderContext === EMPTY_OBJ) {
instance.data = reactive({}) instance.renderContext = reactive({})
} }
currentInstance = null currentInstance = null
} }

View File

@ -1,12 +1,15 @@
import { ComponentInstance } from './component' import { ComponentInstance } from './component'
import { nextTick } from './scheduler' import { nextTick } from './scheduler'
import { instanceWatch } from './apiWatch' import { instanceWatch } from './apiWatch'
import { EMPTY_OBJ } from '@vue/shared'
export const RenderProxyHandlers = { export const RenderProxyHandlers = {
get(target: ComponentInstance, key: string) { get(target: ComponentInstance, key: string) {
const { data, props, propsProxy } = target const { renderContext, data, props, propsProxy } = target
if (data.hasOwnProperty(key)) { if (data !== EMPTY_OBJ && data.hasOwnProperty(key)) {
return data[key] return data[key]
} else if (renderContext.hasOwnProperty(key)) {
return renderContext[key]
} else if (props.hasOwnProperty(key)) { } else if (props.hasOwnProperty(key)) {
// return the value from propsProxy for ref unwrapping and readonly // return the value from propsProxy for ref unwrapping and readonly
return (propsProxy as any)[key] return (propsProxy as any)[key]
@ -31,7 +34,6 @@ export const RenderProxyHandlers = {
case '$el': case '$el':
return target.vnode.el return target.vnode.el
case '$options': case '$options':
// TODO handle merging
return target.type return target.type
default: default:
// methods are only exposed when options are supported // methods are only exposed when options are supported
@ -50,10 +52,11 @@ export const RenderProxyHandlers = {
} }
}, },
set(target: ComponentInstance, key: string, value: any): boolean { set(target: ComponentInstance, key: string, value: any): boolean {
const { data } = target const { data, renderContext } = target
if (data.hasOwnProperty(key)) { if (data !== EMPTY_OBJ && data.hasOwnProperty(key)) {
data[key] = value data[key] = value
return true } else if (renderContext.hasOwnProperty(key)) {
renderContext[key] = value
} else if (key[0] === '$' && key.slice(1) in target) { } else if (key[0] === '$' && key.slice(1) in target) {
// TODO warn attempt of mutating public property // TODO warn attempt of mutating public property
return false return false
@ -62,7 +65,7 @@ export const RenderProxyHandlers = {
return false return false
} else { } else {
target.user[key] = value target.user[key] = value
}
return true return true
} }
}
} }

View File

@ -1127,13 +1127,13 @@ export function createRenderer(options: RendererOptions): RootRenderFunction {
value: HostNode | ComponentInstance | null value: HostNode | ComponentInstance | null
) { ) {
const refs = parent.refs === EMPTY_OBJ ? (parent.refs = {}) : parent.refs const refs = parent.refs === EMPTY_OBJ ? (parent.refs = {}) : parent.refs
const rawData = toRaw(parent.data) const renderContext = toRaw(parent.renderContext)
// unset old ref // unset old ref
if (oldRef !== null && oldRef !== ref) { if (oldRef !== null && oldRef !== ref) {
if (isString(oldRef)) { if (isString(oldRef)) {
refs[oldRef] = null refs[oldRef] = null
const oldSetupRef = rawData[oldRef] const oldSetupRef = renderContext[oldRef]
if (isRef(oldSetupRef)) { if (isRef(oldSetupRef)) {
oldSetupRef.value = null oldSetupRef.value = null
} }
@ -1143,7 +1143,7 @@ export function createRenderer(options: RendererOptions): RootRenderFunction {
} }
if (isString(ref)) { if (isString(ref)) {
const setupRef = rawData[ref] const setupRef = renderContext[ref]
if (isRef(setupRef)) { if (isRef(setupRef)) {
setupRef.value = value setupRef.value = value
} }