wip(ssr): reduce reactivity overhead during ssr

This commit is contained in:
Evan You 2020-01-24 11:39:52 -05:00
parent cee36ad028
commit 25a0d4a65f
7 changed files with 50 additions and 21 deletions

View File

@ -56,6 +56,10 @@ export function computed<T>(
// expose effect so computed can be stopped // expose effect so computed can be stopped
effect: runner, effect: runner,
get value() { get value() {
if (__SSR__) {
return getter()
}
if (dirty) { if (dirty) {
value = runner() value = runner()
dirty = false dirty = false

View File

@ -1,4 +1,4 @@
import { isObject, toRawType } from '@vue/shared' import { isObject, toRawType, EMPTY_OBJ } from '@vue/shared'
import { import {
mutableHandlers, mutableHandlers,
readonlyHandlers, readonlyHandlers,
@ -117,9 +117,15 @@ function createReactiveObject(
if (!canObserve(target)) { if (!canObserve(target)) {
return target return target
} }
const handlers = collectionTypes.has(target.constructor) const handlers = __SSR__
? collectionHandlers ? // disable reactivity in SSR.
: baseHandlers // NOTE: a potential caveat here is isReactive check may return different
// values on nested values on client/server. This should be very rare but
// we should keep an eye on this.
EMPTY_OBJ
: collectionTypes.has(target.constructor)
? collectionHandlers
: baseHandlers
observed = new Proxy(target, handlers) observed = new Proxy(target, handlers)
toProxy.set(target, observed) toProxy.set(target, observed)
toRaw.set(observed, target) toRaw.set(observed, target)

View File

@ -36,6 +36,14 @@ export function ref(raw?: unknown) {
return raw return raw
} }
raw = convert(raw) raw = convert(raw)
if (__SSR__) {
return {
_isRef: true,
value: raw
}
}
const r = { const r = {
_isRef: true, _isRef: true,
get value() { get value() {
@ -58,7 +66,7 @@ export function ref(raw?: unknown) {
export function toRefs<T extends object>( export function toRefs<T extends object>(
object: T object: T
): { [K in keyof T]: Ref<T[K]> } { ): { [K in keyof T]: Ref<T[K]> } {
if (__DEV__ && !isReactive(object)) { if (__DEV__ && !__SSR__ && !isReactive(object)) {
console.warn(`toRefs() expects a reactive object but received a plain one.`) console.warn(`toRefs() expects a reactive object but received a plain one.`)
} }
const ret: any = {} const ret: any = {}

View File

@ -65,7 +65,8 @@ export function injectHook(
export const createHook = <T extends Function = () => any>( export const createHook = <T extends Function = () => any>(
lifecycle: LifecycleHooks lifecycle: LifecycleHooks
) => (hook: T, target: ComponentInternalInstance | null = currentInstance) => ) => (hook: T, target: ComponentInternalInstance | null = currentInstance) =>
injectHook(lifecycle, hook, target) // post-create lifecycle registrations are noops during SSR
!__SSR__ && injectHook(lifecycle, hook, target)
export const onBeforeMount = createHook(LifecycleHooks.BEFORE_MOUNT) export const onBeforeMount = createHook(LifecycleHooks.BEFORE_MOUNT)
export const onMounted = createHook(LifecycleHooks.MOUNTED) export const onMounted = createHook(LifecycleHooks.MOUNTED)
@ -87,6 +88,10 @@ export type ErrorCapturedHook = (
instance: ComponentPublicInstance | null, instance: ComponentPublicInstance | null,
info: string info: string
) => boolean | void ) => boolean | void
export const onErrorCaptured = createHook<ErrorCapturedHook>(
LifecycleHooks.ERROR_CAPTURED export const onErrorCaptured = (
) hook: ErrorCapturedHook,
target: ComponentInternalInstance | null = currentInstance
) => {
injectHook(LifecycleHooks.ERROR_CAPTURED, hook, target)
}

View File

@ -218,7 +218,7 @@ export function applyOptions(
) { ) {
const renderContext = const renderContext =
instance.renderContext === EMPTY_OBJ instance.renderContext === EMPTY_OBJ
? (instance.renderContext = reactive({})) ? (instance.renderContext = __SSR__ ? {} : reactive({}))
: instance.renderContext : instance.renderContext
const ctx = instance.proxy! const ctx = instance.proxy!
const { const {
@ -285,7 +285,7 @@ export function applyOptions(
checkDuplicateProperties!(OptionTypes.DATA, key) checkDuplicateProperties!(OptionTypes.DATA, key)
} }
} }
instance.data = reactive(data) instance.data = __SSR__ ? data : reactive(data)
} else { } else {
// existing data: this is a mixin or extends. // existing data: this is a mixin or extends.
extend(instance.data, data) extend(instance.data, data)

View File

@ -13,7 +13,8 @@ import {
isArray, isArray,
isFunction, isFunction,
isString, isString,
hasChanged hasChanged,
NOOP
} from '@vue/shared' } from '@vue/shared'
import { recordEffect } from './apiReactivity' import { recordEffect } from './apiReactivity'
import { import {
@ -85,7 +86,10 @@ export function watch<T = any>(
cbOrOptions?: WatchCallback<T> | WatchOptions, cbOrOptions?: WatchCallback<T> | WatchOptions,
options?: WatchOptions options?: WatchOptions
): StopHandle { ): StopHandle {
if (isFunction(cbOrOptions)) { if (__SSR__ && !(options && options.flush === 'sync')) {
// during SSR, non-sync watchers never fire.
return NOOP
} else if (isFunction(cbOrOptions)) {
// effect callback as 2nd argument - this is a source watcher // effect callback as 2nd argument - this is a source watcher
return doWatch(effectOrSource, cbOrOptions, options) return doWatch(effectOrSource, cbOrOptions, options)
} else { } else {

View File

@ -288,7 +288,6 @@ function setupStatefulComponent(
instance: ComponentInternalInstance, instance: ComponentInternalInstance,
parentSuspense: SuspenseBoundary | null parentSuspense: SuspenseBoundary | null
) { ) {
let setupResult
const Component = instance.type as ComponentOptions const Component = instance.type as ComponentOptions
if (__DEV__) { if (__DEV__) {
@ -315,7 +314,9 @@ function setupStatefulComponent(
// 2. create props proxy // 2. create props proxy
// the propsProxy is a reactive AND readonly proxy to the actual props. // the propsProxy is a reactive AND readonly proxy to the actual props.
// it will be updated in resolveProps() on updates before render // it will be updated in resolveProps() on updates before render
const propsProxy = (instance.propsProxy = shallowReadonly(instance.props)) const propsProxy = (instance.propsProxy = __SSR__
? instance.props
: shallowReadonly(instance.props))
// 3. call setup() // 3. call setup()
const { setup } = Component const { setup } = Component
if (setup) { if (setup) {
@ -324,7 +325,7 @@ function setupStatefulComponent(
currentInstance = instance currentInstance = instance
currentSuspense = parentSuspense currentSuspense = parentSuspense
setupResult = callWithErrorHandling( const setupResult = callWithErrorHandling(
setup, setup,
instance, instance,
ErrorCodes.SETUP_FUNCTION, ErrorCodes.SETUP_FUNCTION,
@ -334,7 +335,10 @@ function setupStatefulComponent(
currentSuspense = null currentSuspense = null
if (isPromise(setupResult)) { if (isPromise(setupResult)) {
if (__FEATURE_SUSPENSE__) { if (__SSR__) {
// return the promise so server-renderer can wait on it
return setupResult
} else if (__FEATURE_SUSPENSE__) {
// async setup returned Promise. // async setup returned Promise.
// bail here and wait for re-entry. // bail here and wait for re-entry.
instance.asyncDep = setupResult instance.asyncDep = setupResult
@ -350,8 +354,6 @@ function setupStatefulComponent(
} else { } else {
finishComponentSetup(instance, parentSuspense) finishComponentSetup(instance, parentSuspense)
} }
return setupResult
} }
export function handleSetupResult( export function handleSetupResult(
@ -371,7 +373,7 @@ export function handleSetupResult(
} }
// setup returned bindings. // setup returned bindings.
// assuming a render function compiled from template is present. // assuming a render function compiled from template is present.
instance.renderContext = reactive(setupResult) instance.renderContext = __SSR__ ? setupResult : 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: ${
@ -449,7 +451,7 @@ function finishComponentSetup(
} }
if (instance.renderContext === EMPTY_OBJ) { if (instance.renderContext === EMPTY_OBJ) {
instance.renderContext = reactive({}) instance.renderContext = __SSR__ ? {} : reactive({})
} }
} }