Skip to content

Commit

Permalink
refactor: remove implicit reactive() call on renderContext
Browse files Browse the repository at this point in the history
reference: vuejs/rfcs#121

BREAKING CHANGE: object returned from `setup()` are no longer implicitly
passed to `reactive()`.

  The renderContext is the object returned by `setup()` (or a new object
  if no setup() is present). Before this change, it was implicitly passed
  to `reactive()` for ref unwrapping. But this has the side effect of
  unnecessary deep reactive conversion on properties that should not be
  made reactive (e.g. computed return values and injected non-reactive
  objects), and can lead to performance issues.

  This change removes the `reactive()` call and instead performs a
  shallow ref unwrapping at the render proxy level. The breaking part is
  when the user returns an object with a plain property from `setup()`,
  e.g. `return { count: 0 }`, this property will no longer trigger
  updates when mutated by a in-template event handler. Instead, explicit
  refs are required.

  This also means that any objects not explicitly made reactive in
  `setup()` will remain non-reactive. This can be desirable when
  exposing heavy external stateful objects on `this`.
  • Loading branch information
yyx990803 committed Jan 27, 2020
1 parent 763faac commit 6b10f0c
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 12 deletions.
15 changes: 10 additions & 5 deletions packages/runtime-core/src/apiOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,10 +216,6 @@ export function applyOptions(
options: ComponentOptions,
asMixin: boolean = false
) {
const renderContext =
instance.renderContext === EMPTY_OBJ
? (instance.renderContext = __SSR__ ? {} : reactive({}))
: instance.renderContext
const ctx = instance.proxy!
const {
// composition
Expand Down Expand Up @@ -250,6 +246,11 @@ export function applyOptions(
errorCaptured
} = options

const renderContext =
instance.renderContext === EMPTY_OBJ
? (instance.renderContext = {})
: instance.renderContext

const globalMixins = instance.appContext.mixins
// call it only during dev
const checkDuplicateProperties = __DEV__ ? createDuplicateChecker() : null
Expand Down Expand Up @@ -285,12 +286,13 @@ export function applyOptions(
checkDuplicateProperties!(OptionTypes.DATA, key)
}
}
instance.data = __SSR__ ? data : reactive(data)
instance.data = reactive(data)
} else {
// existing data: this is a mixin or extends.
extend(instance.data, data)
}
}

if (computedOptions) {
for (const key in computedOptions) {
const opt = (computedOptions as ComputedOptions)[key]
Expand Down Expand Up @@ -335,11 +337,13 @@ export function applyOptions(
}
}
}

if (watchOptions) {
for (const key in watchOptions) {
createWatcher(watchOptions[key], renderContext, ctx, key)
}
}

if (provideOptions) {
const provides = isFunction(provideOptions)
? provideOptions.call(ctx)
Expand All @@ -348,6 +352,7 @@ export function applyOptions(
provide(key, provides[key])
}
}

if (injectOptions) {
if (isArray(injectOptions)) {
for (let i = 0; i < injectOptions.length; i++) {
Expand Down
6 changes: 3 additions & 3 deletions packages/runtime-core/src/component.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { VNode, VNodeChild, isVNode } from './vnode'
import { ReactiveEffect, reactive, shallowReadonly } from '@vue/reactivity'
import { ReactiveEffect, shallowReadonly } from '@vue/reactivity'
import {
PublicInstanceProxyHandlers,
ComponentPublicInstance,
Expand Down Expand Up @@ -375,7 +375,7 @@ export function handleSetupResult(
}
// setup returned bindings.
// assuming a render function compiled from template is present.
instance.renderContext = __SSR__ ? setupResult : reactive(setupResult)
instance.renderContext = setupResult
} else if (__DEV__ && setupResult !== undefined) {
warn(
`setup() should return an object. Received: ${
Expand Down Expand Up @@ -453,7 +453,7 @@ function finishComponentSetup(
}

if (instance.renderContext === EMPTY_OBJ) {
instance.renderContext = __SSR__ ? {} : reactive({})
instance.renderContext = {}
}
}

Expand Down
16 changes: 12 additions & 4 deletions packages/runtime-core/src/componentProxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
ComputedOptions,
MethodOptions
} from './apiOptions'
import { UnwrapRef, ReactiveEffect } from '@vue/reactivity'
import { UnwrapRef, ReactiveEffect, isRef, toRaw } from '@vue/reactivity'
import { warn } from './warning'
import { Slots } from './componentSlots'
import {
Expand Down Expand Up @@ -73,6 +73,8 @@ const enum AccessTypes {
OTHER
}

const unwrapRef = (val: unknown) => (isRef(val) ? val.value : val)

export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
get(target: ComponentInternalInstance, key: string) {
// fast path for unscopables when using `with` block
Expand Down Expand Up @@ -102,7 +104,7 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
case AccessTypes.DATA:
return data[key]
case AccessTypes.CONTEXT:
return renderContext[key]
return unwrapRef(renderContext[key])
case AccessTypes.PROPS:
return propsProxy![key]
// default: just fallthrough
Expand All @@ -112,7 +114,7 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
return data[key]
} else if (hasOwn(renderContext, key)) {
accessCache![key] = AccessTypes.CONTEXT
return renderContext[key]
return unwrapRef(renderContext[key])
} else if (type.props != null) {
// only cache other properties when instance has declared (this stable)
// props
Expand Down Expand Up @@ -167,7 +169,13 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
if (data !== EMPTY_OBJ && hasOwn(data, key)) {
data[key] = value
} else if (hasOwn(renderContext, key)) {
renderContext[key] = value
const oldValue = renderContext[key]
value = toRaw(value)
if (isRef(oldValue) && !isRef(value)) {
oldValue.value = value
} else {
renderContext[key] = value
}
} else if (key[0] === '$' && key.slice(1) in target) {
__DEV__ &&
warn(
Expand Down

0 comments on commit 6b10f0c

Please sign in to comment.