Skip to content

Commit 25a0d4a

Browse files
committed
wip(ssr): reduce reactivity overhead during ssr
1 parent cee36ad commit 25a0d4a

File tree

7 files changed

+50
-21
lines changed

7 files changed

+50
-21
lines changed

packages/reactivity/src/computed.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ export function computed<T>(
5656
// expose effect so computed can be stopped
5757
effect: runner,
5858
get value() {
59+
if (__SSR__) {
60+
return getter()
61+
}
62+
5963
if (dirty) {
6064
value = runner()
6165
dirty = false

packages/reactivity/src/reactive.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { isObject, toRawType } from '@vue/shared'
1+
import { isObject, toRawType, EMPTY_OBJ } from '@vue/shared'
22
import {
33
mutableHandlers,
44
readonlyHandlers,
@@ -117,9 +117,15 @@ function createReactiveObject(
117117
if (!canObserve(target)) {
118118
return target
119119
}
120-
const handlers = collectionTypes.has(target.constructor)
121-
? collectionHandlers
122-
: baseHandlers
120+
const handlers = __SSR__
121+
? // disable reactivity in SSR.
122+
// NOTE: a potential caveat here is isReactive check may return different
123+
// values on nested values on client/server. This should be very rare but
124+
// we should keep an eye on this.
125+
EMPTY_OBJ
126+
: collectionTypes.has(target.constructor)
127+
? collectionHandlers
128+
: baseHandlers
123129
observed = new Proxy(target, handlers)
124130
toProxy.set(target, observed)
125131
toRaw.set(observed, target)

packages/reactivity/src/ref.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,14 @@ export function ref(raw?: unknown) {
3636
return raw
3737
}
3838
raw = convert(raw)
39+
40+
if (__SSR__) {
41+
return {
42+
_isRef: true,
43+
value: raw
44+
}
45+
}
46+
3947
const r = {
4048
_isRef: true,
4149
get value() {
@@ -58,7 +66,7 @@ export function ref(raw?: unknown) {
5866
export function toRefs<T extends object>(
5967
object: T
6068
): { [K in keyof T]: Ref<T[K]> } {
61-
if (__DEV__ && !isReactive(object)) {
69+
if (__DEV__ && !__SSR__ && !isReactive(object)) {
6270
console.warn(`toRefs() expects a reactive object but received a plain one.`)
6371
}
6472
const ret: any = {}

packages/runtime-core/src/apiLifecycle.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ export function injectHook(
6565
export const createHook = <T extends Function = () => any>(
6666
lifecycle: LifecycleHooks
6767
) => (hook: T, target: ComponentInternalInstance | null = currentInstance) =>
68-
injectHook(lifecycle, hook, target)
68+
// post-create lifecycle registrations are noops during SSR
69+
!__SSR__ && injectHook(lifecycle, hook, target)
6970

7071
export const onBeforeMount = createHook(LifecycleHooks.BEFORE_MOUNT)
7172
export const onMounted = createHook(LifecycleHooks.MOUNTED)
@@ -87,6 +88,10 @@ export type ErrorCapturedHook = (
8788
instance: ComponentPublicInstance | null,
8889
info: string
8990
) => boolean | void
90-
export const onErrorCaptured = createHook<ErrorCapturedHook>(
91-
LifecycleHooks.ERROR_CAPTURED
92-
)
91+
92+
export const onErrorCaptured = (
93+
hook: ErrorCapturedHook,
94+
target: ComponentInternalInstance | null = currentInstance
95+
) => {
96+
injectHook(LifecycleHooks.ERROR_CAPTURED, hook, target)
97+
}

packages/runtime-core/src/apiOptions.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ export function applyOptions(
218218
) {
219219
const renderContext =
220220
instance.renderContext === EMPTY_OBJ
221-
? (instance.renderContext = reactive({}))
221+
? (instance.renderContext = __SSR__ ? {} : reactive({}))
222222
: instance.renderContext
223223
const ctx = instance.proxy!
224224
const {
@@ -285,7 +285,7 @@ export function applyOptions(
285285
checkDuplicateProperties!(OptionTypes.DATA, key)
286286
}
287287
}
288-
instance.data = reactive(data)
288+
instance.data = __SSR__ ? data : reactive(data)
289289
} else {
290290
// existing data: this is a mixin or extends.
291291
extend(instance.data, data)

packages/runtime-core/src/apiWatch.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ import {
1313
isArray,
1414
isFunction,
1515
isString,
16-
hasChanged
16+
hasChanged,
17+
NOOP
1718
} from '@vue/shared'
1819
import { recordEffect } from './apiReactivity'
1920
import {
@@ -85,7 +86,10 @@ export function watch<T = any>(
8586
cbOrOptions?: WatchCallback<T> | WatchOptions,
8687
options?: WatchOptions
8788
): StopHandle {
88-
if (isFunction(cbOrOptions)) {
89+
if (__SSR__ && !(options && options.flush === 'sync')) {
90+
// during SSR, non-sync watchers never fire.
91+
return NOOP
92+
} else if (isFunction(cbOrOptions)) {
8993
// effect callback as 2nd argument - this is a source watcher
9094
return doWatch(effectOrSource, cbOrOptions, options)
9195
} else {

packages/runtime-core/src/component.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,6 @@ function setupStatefulComponent(
288288
instance: ComponentInternalInstance,
289289
parentSuspense: SuspenseBoundary | null
290290
) {
291-
let setupResult
292291
const Component = instance.type as ComponentOptions
293292

294293
if (__DEV__) {
@@ -315,7 +314,9 @@ function setupStatefulComponent(
315314
// 2. create props proxy
316315
// the propsProxy is a reactive AND readonly proxy to the actual props.
317316
// it will be updated in resolveProps() on updates before render
318-
const propsProxy = (instance.propsProxy = shallowReadonly(instance.props))
317+
const propsProxy = (instance.propsProxy = __SSR__
318+
? instance.props
319+
: shallowReadonly(instance.props))
319320
// 3. call setup()
320321
const { setup } = Component
321322
if (setup) {
@@ -324,7 +325,7 @@ function setupStatefulComponent(
324325

325326
currentInstance = instance
326327
currentSuspense = parentSuspense
327-
setupResult = callWithErrorHandling(
328+
const setupResult = callWithErrorHandling(
328329
setup,
329330
instance,
330331
ErrorCodes.SETUP_FUNCTION,
@@ -334,7 +335,10 @@ function setupStatefulComponent(
334335
currentSuspense = null
335336

336337
if (isPromise(setupResult)) {
337-
if (__FEATURE_SUSPENSE__) {
338+
if (__SSR__) {
339+
// return the promise so server-renderer can wait on it
340+
return setupResult
341+
} else if (__FEATURE_SUSPENSE__) {
338342
// async setup returned Promise.
339343
// bail here and wait for re-entry.
340344
instance.asyncDep = setupResult
@@ -350,8 +354,6 @@ function setupStatefulComponent(
350354
} else {
351355
finishComponentSetup(instance, parentSuspense)
352356
}
353-
354-
return setupResult
355357
}
356358

357359
export function handleSetupResult(
@@ -371,7 +373,7 @@ export function handleSetupResult(
371373
}
372374
// setup returned bindings.
373375
// assuming a render function compiled from template is present.
374-
instance.renderContext = reactive(setupResult)
376+
instance.renderContext = __SSR__ ? setupResult : reactive(setupResult)
375377
} else if (__DEV__ && setupResult !== undefined) {
376378
warn(
377379
`setup() should return an object. Received: ${
@@ -449,7 +451,7 @@ function finishComponentSetup(
449451
}
450452

451453
if (instance.renderContext === EMPTY_OBJ) {
452-
instance.renderContext = reactive({})
454+
instance.renderContext = __SSR__ ? {} : reactive({})
453455
}
454456
}
455457

0 commit comments

Comments
 (0)