Skip to content

Commit

Permalink
wip: separate setupState
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed Apr 16, 2020
1 parent 0709380 commit b2662a6
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 41 deletions.
4 changes: 2 additions & 2 deletions packages/runtime-core/__tests__/componentProxy.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ describe('component: proxy', () => {
expect(instance!.data.foo).toBe(2)
})

test('renderContext', () => {
test('setupState', () => {
let instance: ComponentInternalInstance
let instanceProxy: any
const Comp = {
Expand All @@ -55,7 +55,7 @@ describe('component: proxy', () => {
render(h(Comp), nodeOps.createElement('div'))
expect(instanceProxy.foo).toBe(1)
instanceProxy.foo = 2
expect(instance!.renderContext.foo).toBe(2)
expect(instance!.setupState.foo).toBe(2)
})

test('should not expose non-declared props', () => {
Expand Down
21 changes: 13 additions & 8 deletions packages/runtime-core/src/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
RuntimeCompiledPublicInstanceProxyHandlers,
createDevProxyTarget,
exposePropsOnDevProxyTarget,
exposeRenderContextOnDevProxyTarget
exposeSetupStateOnDevProxyTarget
} from './componentProxy'
import { ComponentPropsOptions, initProps } from './componentProps'
import { Slots, initSlots, InternalSlots } from './componentSlots'
Expand Down Expand Up @@ -143,6 +143,13 @@ export interface ComponentInternalInstance {
attrs: Data
slots: InternalSlots
proxy: ComponentPublicInstance | null
refs: Data
emit: EmitFn

// setup
setupState: Data
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
Expand All @@ -152,9 +159,6 @@ export interface ComponentInternalInstance {
// alternative proxy used only for runtime-compiled render functions using
// `with` block
withProxy: ComponentPublicInstance | null
setupContext: SetupContext | null
refs: Data
emit: EmitFn

// suspense related
suspense: SuspenseBoundary | null
Expand Down Expand Up @@ -209,19 +213,20 @@ export function createComponentInstance(
proxy: null,
proxyTarget: null!, // to be immediately set
withProxy: null,
setupContext: null,
effects: null,
provides: parent ? parent.provides : Object.create(appContext.provides),
accessCache: null!,
renderCache: [],

// setup context properties
// state
renderContext: EMPTY_OBJ,
data: EMPTY_OBJ,
props: EMPTY_OBJ,
attrs: EMPTY_OBJ,
slots: EMPTY_OBJ,
refs: EMPTY_OBJ,
setupState: EMPTY_OBJ,
setupContext: null,

// per-instance asset storage (mutable during options resolution)
components: Object.create(appContext.components),
Expand Down Expand Up @@ -392,9 +397,9 @@ export function handleSetupResult(
}
// setup returned bindings.
// assuming a render function compiled from template is present.
instance.renderContext = reactive(setupResult)
instance.setupState = reactive(setupResult)
if (__DEV__) {
exposeRenderContextOnDevProxyTarget(instance)
exposeSetupStateOnDevProxyTarget(instance)
}
} else if (__DEV__ && setupResult !== undefined) {
warn(
Expand Down
70 changes: 44 additions & 26 deletions packages/runtime-core/src/componentProxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,10 @@ const publicPropertiesMap: Record<
}

const enum AccessTypes {
SETUP,
DATA,
CONTEXT,
PROPS,
CONTEXT,
OTHER
}

Expand All @@ -91,6 +92,7 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
get({ _: instance }: ComponentPublicProxyTarget, key: string) {
const {
renderContext,
setupState,
data,
props,
accessCache,
Expand All @@ -109,6 +111,8 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
const n = accessCache![key]
if (n !== undefined) {
switch (n) {
case AccessTypes.SETUP:
return setupState[key]
case AccessTypes.DATA:
return data[key]
case AccessTypes.CONTEXT:
Expand All @@ -117,22 +121,25 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
return props![key]
// default: just fallthrough
}
} else if (setupState !== EMPTY_OBJ && hasOwn(setupState, key)) {
accessCache![key] = AccessTypes.SETUP
return setupState[key]
} else if (data !== EMPTY_OBJ && hasOwn(data, key)) {
accessCache![key] = AccessTypes.DATA
return data[key]
} else if (
// only cache other properties when instance has declared (thus stable)
// props
type.props &&
hasOwn(normalizePropsOptions(type.props)[0]!, key)
) {
accessCache![key] = AccessTypes.PROPS
return props![key]
} else if (renderContext !== EMPTY_OBJ && hasOwn(renderContext, key)) {
accessCache![key] = AccessTypes.CONTEXT
return renderContext[key]
} else if (type.props) {
// only cache other properties when instance has declared (thus stable)
// props
if (hasOwn(normalizePropsOptions(type.props)[0]!, key)) {
accessCache![key] = AccessTypes.PROPS
// return the value from propsProxy for ref unwrapping and readonly
return props![key]
} else {
accessCache![key] = AccessTypes.OTHER
}
} else {
accessCache![key] = AccessTypes.OTHER
}
}

Expand Down Expand Up @@ -170,23 +177,25 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
key: string,
value: any
): boolean {
const { data, renderContext } = instance
if (data !== EMPTY_OBJ && hasOwn(data, key)) {
const { data, setupState, renderContext } = instance
if (setupState !== EMPTY_OBJ && hasOwn(setupState, key)) {
setupState[key] = value
} else 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 instance) {
} else if (key in instance.props) {
__DEV__ &&
warn(
`Attempting to mutate public property "${key}". ` +
`Properties starting with $ are reserved and readonly.`,
`Attempting to mutate prop "${key}". Props are readonly.`,
instance
)
return false
} else if (key in instance.props) {
} else if (hasOwn(renderContext, key)) {
renderContext[key] = value
} else if (key[0] === '$' && key.slice(1) in instance) {
__DEV__ &&
warn(
`Attempting to mutate prop "${key}". Props are readonly.`,
`Attempting to mutate public property "${key}". ` +
`Properties starting with $ are reserved and readonly.`,
instance
)
return false
Expand All @@ -206,15 +215,24 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {

has(
{
_: { data, accessCache, renderContext, type, proxyTarget, appContext }
_: {
data,
setupState,
accessCache,
renderContext,
type,
proxyTarget,
appContext
}
}: ComponentPublicProxyTarget,
key: string
) {
return (
accessCache![key] !== undefined ||
(data !== EMPTY_OBJ && hasOwn(data, key)) ||
hasOwn(renderContext, key) ||
(setupState !== EMPTY_OBJ && hasOwn(setupState, key)) ||
(type.props && hasOwn(normalizePropsOptions(type.props)[0]!, key)) ||
hasOwn(renderContext, key) ||
hasOwn(publicPropertiesMap, key) ||
hasOwn(proxyTarget, key) ||
hasOwn(appContext.config.globalProperties, key)
Expand Down Expand Up @@ -306,15 +324,15 @@ export function exposePropsOnDevProxyTarget(
}
}

export function exposeRenderContextOnDevProxyTarget(
export function exposeSetupStateOnDevProxyTarget(
instance: ComponentInternalInstance
) {
const { proxyTarget, renderContext } = instance
Object.keys(toRaw(renderContext)).forEach(key => {
const { proxyTarget, setupState } = instance
Object.keys(toRaw(setupState)).forEach(key => {
Object.defineProperty(proxyTarget, key, {
enumerable: true,
configurable: true,
get: () => renderContext[key],
get: () => setupState[key],
set: NOOP
})
})
Expand Down
10 changes: 5 additions & 5 deletions packages/runtime-core/src/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1899,14 +1899,14 @@ function baseCreateRenderer(
}
const oldRef = oldRawRef && oldRawRef[1]
const refs = owner.refs === EMPTY_OBJ ? (owner.refs = {}) : owner.refs
const renderContext = owner.renderContext
const setupState = owner.setupState

// unset old ref
if (oldRef != null && oldRef !== ref) {
if (isString(oldRef)) {
refs[oldRef] = null
if (hasOwn(renderContext, oldRef)) {
renderContext[oldRef] = null
if (hasOwn(setupState, oldRef)) {
setupState[oldRef] = null
}
} else if (isRef(oldRef)) {
oldRef.value = null
Expand All @@ -1915,8 +1915,8 @@ function baseCreateRenderer(

if (isString(ref)) {
refs[ref] = value
if (hasOwn(renderContext, ref)) {
renderContext[ref] = value
if (hasOwn(setupState, ref)) {
setupState[ref] = value
}
} else if (isRef(ref)) {
ref.value = value
Expand Down

0 comments on commit b2662a6

Please sign in to comment.