refactor(runtime-core): refactor instance public proxy context object

This commit is contained in:
Evan You
2020-04-16 12:49:50 -04:00
parent b2662a62c5
commit 370fc820cc
6 changed files with 131 additions and 154 deletions

View File

@@ -39,9 +39,7 @@ import {
import {
reactive,
ComputedGetter,
WritableComputedOptions,
ComputedRef,
toRaw
WritableComputedOptions
} from '@vue/reactivity'
import {
ComponentObjectPropsOptions,
@@ -246,8 +244,7 @@ export function applyOptions(
options: ComponentOptions,
asMixin: boolean = false
) {
const proxyTarget = instance.proxyTarget
const ctx = instance.proxy!
const publicThis = instance.proxy!
const {
// composition
mixins,
@@ -277,19 +274,13 @@ export function applyOptions(
errorCaptured
} = options
const renderContext = toRaw(
instance.renderContext === EMPTY_OBJ &&
(computedOptions || methods || watchOptions || injectOptions)
? (instance.renderContext = reactive({}))
: instance.renderContext
)
const ctx = instance.ctx
const globalMixins = instance.appContext.mixins
// call it only during dev
// applyOptions is called non-as-mixin once per instance
if (!asMixin) {
callSyncHook('beforeCreate', options, ctx, globalMixins)
callSyncHook('beforeCreate', options, publicThis, globalMixins)
// global mixins are applied first
applyMixins(instance, globalMixins)
}
@@ -318,7 +309,7 @@ export function applyOptions(
`Plain object usage is no longer supported.`
)
}
const data = dataOptions.call(ctx, ctx)
const data = dataOptions.call(publicThis, publicThis)
if (__DEV__ && isPromise(data)) {
warn(
`data() returned a Promise - note data() cannot be async; If you ` +
@@ -332,7 +323,13 @@ export function applyOptions(
if (__DEV__) {
for (const key in data) {
checkDuplicateProperties!(OptionTypes.DATA, key)
if (!(key in proxyTarget)) proxyTarget[key] = data[key]
// expose data on ctx during dev
Object.defineProperty(ctx, key, {
configurable: true,
enumerable: true,
get: () => data[key],
set: NOOP
})
}
}
instance.data = reactive(data)
@@ -345,37 +342,36 @@ export function applyOptions(
if (computedOptions) {
for (const key in computedOptions) {
const opt = (computedOptions as ComputedOptions)[key]
if (isFunction(opt)) {
renderContext[key] = computed(opt.bind(ctx, ctx))
} else {
const { get, set } = opt
if (isFunction(get)) {
renderContext[key] = computed({
get: get.bind(ctx, ctx),
set: isFunction(set)
? set.bind(ctx)
: __DEV__
? () => {
warn(
`Write operation failed: computed property "${key}" is readonly.`
)
}
: NOOP
})
} else if (__DEV__) {
warn(`Computed property "${key}" has no getter.`)
}
const get = isFunction(opt)
? opt.bind(publicThis, publicThis)
: isFunction(opt.get)
? opt.get.bind(publicThis, publicThis)
: NOOP
if (__DEV__ && get === NOOP) {
warn(`Computed property "${key}" has no getter.`)
}
const set =
!isFunction(opt) && isFunction(opt.set)
? opt.set.bind(publicThis)
: __DEV__
? () => {
warn(
`Write operation failed: computed property "${key}" is readonly.`
)
}
: NOOP
const c = computed({
get,
set
})
Object.defineProperty(ctx, key, {
enumerable: true,
configurable: true,
get: () => c.value,
set: v => (c.value = v)
})
if (__DEV__) {
checkDuplicateProperties!(OptionTypes.COMPUTED, key)
if (renderContext[key] && !(key in proxyTarget)) {
Object.defineProperty(proxyTarget, key, {
enumerable: true,
configurable: true,
get: () => (renderContext[key] as ComputedRef).value,
set: NOOP
})
}
}
}
}
@@ -384,12 +380,9 @@ export function applyOptions(
for (const key in methods) {
const methodHandler = (methods as MethodOptions)[key]
if (isFunction(methodHandler)) {
renderContext[key] = methodHandler.bind(ctx)
ctx[key] = methodHandler.bind(publicThis)
if (__DEV__) {
checkDuplicateProperties!(OptionTypes.METHODS, key)
if (!(key in proxyTarget)) {
proxyTarget[key] = renderContext[key]
}
}
} else if (__DEV__) {
warn(
@@ -402,13 +395,13 @@ export function applyOptions(
if (watchOptions) {
for (const key in watchOptions) {
createWatcher(watchOptions[key], renderContext, ctx, key)
createWatcher(watchOptions[key], ctx, publicThis, key)
}
}
if (provideOptions) {
const provides = isFunction(provideOptions)
? provideOptions.call(ctx)
? provideOptions.call(publicThis)
: provideOptions
for (const key in provides) {
provide(key, provides[key])
@@ -419,23 +412,21 @@ export function applyOptions(
if (isArray(injectOptions)) {
for (let i = 0; i < injectOptions.length; i++) {
const key = injectOptions[i]
renderContext[key] = inject(key)
ctx[key] = inject(key)
if (__DEV__) {
checkDuplicateProperties!(OptionTypes.INJECT, key)
proxyTarget[key] = renderContext[key]
}
}
} else {
for (const key in injectOptions) {
const opt = injectOptions[key]
if (isObject(opt)) {
renderContext[key] = inject(opt.from, opt.default)
ctx[key] = inject(opt.from, opt.default)
} else {
renderContext[key] = inject(opt)
ctx[key] = inject(opt)
}
if (__DEV__) {
checkDuplicateProperties!(OptionTypes.INJECT, key)
proxyTarget[key] = renderContext[key]
}
}
}
@@ -451,40 +442,40 @@ export function applyOptions(
// lifecycle options
if (!asMixin) {
callSyncHook('created', options, ctx, globalMixins)
callSyncHook('created', options, publicThis, globalMixins)
}
if (beforeMount) {
onBeforeMount(beforeMount.bind(ctx))
onBeforeMount(beforeMount.bind(publicThis))
}
if (mounted) {
onMounted(mounted.bind(ctx))
onMounted(mounted.bind(publicThis))
}
if (beforeUpdate) {
onBeforeUpdate(beforeUpdate.bind(ctx))
onBeforeUpdate(beforeUpdate.bind(publicThis))
}
if (updated) {
onUpdated(updated.bind(ctx))
onUpdated(updated.bind(publicThis))
}
if (activated) {
onActivated(activated.bind(ctx))
onActivated(activated.bind(publicThis))
}
if (deactivated) {
onDeactivated(deactivated.bind(ctx))
onDeactivated(deactivated.bind(publicThis))
}
if (errorCaptured) {
onErrorCaptured(errorCaptured.bind(ctx))
onErrorCaptured(errorCaptured.bind(publicThis))
}
if (renderTracked) {
onRenderTracked(renderTracked.bind(ctx))
onRenderTracked(renderTracked.bind(publicThis))
}
if (renderTriggered) {
onRenderTriggered(renderTriggered.bind(ctx))
onRenderTriggered(renderTriggered.bind(publicThis))
}
if (beforeUnmount) {
onBeforeUnmount(beforeUnmount.bind(ctx))
onBeforeUnmount(beforeUnmount.bind(publicThis))
}
if (unmounted) {
onUnmounted(unmounted.bind(ctx))
onUnmounted(unmounted.bind(publicThis))
}
}
@@ -533,25 +524,25 @@ function applyMixins(
function createWatcher(
raw: ComponentWatchOptionItem,
renderContext: Data,
ctx: ComponentPublicInstance,
ctx: Data,
publicThis: ComponentPublicInstance,
key: string
) {
const getter = () => (ctx as Data)[key]
const getter = () => (publicThis as Data)[key]
if (isString(raw)) {
const handler = renderContext[raw]
const handler = ctx[raw]
if (isFunction(handler)) {
watch(getter, handler as WatchCallback)
} else if (__DEV__) {
warn(`Invalid watch handler specified by key "${raw}"`, handler)
}
} else if (isFunction(raw)) {
watch(getter, raw.bind(ctx))
watch(getter, raw.bind(publicThis))
} else if (isObject(raw)) {
if (isArray(raw)) {
raw.forEach(r => createWatcher(r, renderContext, ctx, key))
raw.forEach(r => createWatcher(r, ctx, publicThis, key))
} else {
watch(getter, raw.handler.bind(ctx), raw)
watch(getter, raw.handler.bind(publicThis), raw)
}
} else if (__DEV__) {
warn(`Invalid watch option: "${key}"`)