]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
wip(ssr): reduce reactivity overhead during ssr
authorEvan You <yyx990803@gmail.com>
Fri, 24 Jan 2020 16:39:52 +0000 (11:39 -0500)
committerEvan You <yyx990803@gmail.com>
Mon, 27 Jan 2020 21:00:17 +0000 (16:00 -0500)
packages/reactivity/src/computed.ts
packages/reactivity/src/reactive.ts
packages/reactivity/src/ref.ts
packages/runtime-core/src/apiLifecycle.ts
packages/runtime-core/src/apiOptions.ts
packages/runtime-core/src/apiWatch.ts
packages/runtime-core/src/component.ts

index 1f1d8ea43d7f76649f7aa941cd7ce8be4deac24c..4e2df4f85c89a7bb605d5c1c3e3f5b1f1cd2f42a 100644 (file)
@@ -56,6 +56,10 @@ export function computed<T>(
     // expose effect so computed can be stopped
     effect: runner,
     get value() {
+      if (__SSR__) {
+        return getter()
+      }
+
       if (dirty) {
         value = runner()
         dirty = false
index 091214481dd93d13a2c86b67ca28507f42b89e0a..256c91e6b491a259bd7e0624b3a5fd0cc7499e96 100644 (file)
@@ -1,4 +1,4 @@
-import { isObject, toRawType } from '@vue/shared'
+import { isObject, toRawType, EMPTY_OBJ } from '@vue/shared'
 import {
   mutableHandlers,
   readonlyHandlers,
@@ -117,9 +117,15 @@ function createReactiveObject(
   if (!canObserve(target)) {
     return target
   }
-  const handlers = collectionTypes.has(target.constructor)
-    ? collectionHandlers
-    : baseHandlers
+  const handlers = __SSR__
+    ? // disable reactivity in SSR.
+      // 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)
   toProxy.set(target, observed)
   toRaw.set(observed, target)
index 5b99194b2e022ec3af12691b62ced68dd1bdf6aa..fccff406fbdb0fa220fd8613d7584a15edb80ec3 100644 (file)
@@ -36,6 +36,14 @@ export function ref(raw?: unknown) {
     return raw
   }
   raw = convert(raw)
+
+  if (__SSR__) {
+    return {
+      _isRef: true,
+      value: raw
+    }
+  }
+
   const r = {
     _isRef: true,
     get value() {
@@ -58,7 +66,7 @@ export function ref(raw?: unknown) {
 export function toRefs<T extends object>(
   object: T
 ): { [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.`)
   }
   const ret: any = {}
index 1bab9ca9576e076738f38513113b3bf726e6379b..0b0c25e2f9b19f6c5fc8c151dd827f4db995cd0e 100644 (file)
@@ -65,7 +65,8 @@ export function injectHook(
 export const createHook = <T extends Function = () => any>(
   lifecycle: LifecycleHooks
 ) => (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 onMounted = createHook(LifecycleHooks.MOUNTED)
@@ -87,6 +88,10 @@ export type ErrorCapturedHook = (
   instance: ComponentPublicInstance | null,
   info: string
 ) => 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)
+}
index 2cccb32c6b48ff8c1711ec7dae0ae2040b823015..9eb3009b2553f9ae9b82bf7e548f32b13a51e3cf 100644 (file)
@@ -218,7 +218,7 @@ export function applyOptions(
 ) {
   const renderContext =
     instance.renderContext === EMPTY_OBJ
-      ? (instance.renderContext = reactive({}))
+      ? (instance.renderContext = __SSR__ ? {} : reactive({}))
       : instance.renderContext
   const ctx = instance.proxy!
   const {
@@ -285,7 +285,7 @@ export function applyOptions(
           checkDuplicateProperties!(OptionTypes.DATA, key)
         }
       }
-      instance.data = reactive(data)
+      instance.data = __SSR__ ? data : reactive(data)
     } else {
       // existing data: this is a mixin or extends.
       extend(instance.data, data)
index 4760344c4af15eaa3eff8887c40b012a0c9b35b8..000f48edb1c15c7ffb46bfbcd89b6b8b7bebbd2d 100644 (file)
@@ -13,7 +13,8 @@ import {
   isArray,
   isFunction,
   isString,
-  hasChanged
+  hasChanged,
+  NOOP
 } from '@vue/shared'
 import { recordEffect } from './apiReactivity'
 import {
@@ -85,7 +86,10 @@ export function watch<T = any>(
   cbOrOptions?: WatchCallback<T> | WatchOptions,
   options?: WatchOptions
 ): 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
     return doWatch(effectOrSource, cbOrOptions, options)
   } else {
index 77f7678737427f0071cdb9d844ba1cc004970621..8117d68d7c6d9791422226adb86f30cbcc87f798 100644 (file)
@@ -288,7 +288,6 @@ function setupStatefulComponent(
   instance: ComponentInternalInstance,
   parentSuspense: SuspenseBoundary | null
 ) {
-  let setupResult
   const Component = instance.type as ComponentOptions
 
   if (__DEV__) {
@@ -315,7 +314,9 @@ function setupStatefulComponent(
   // 2. create props proxy
   // the propsProxy is a reactive AND readonly proxy to the actual props.
   // 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()
   const { setup } = Component
   if (setup) {
@@ -324,7 +325,7 @@ function setupStatefulComponent(
 
     currentInstance = instance
     currentSuspense = parentSuspense
-    setupResult = callWithErrorHandling(
+    const setupResult = callWithErrorHandling(
       setup,
       instance,
       ErrorCodes.SETUP_FUNCTION,
@@ -334,7 +335,10 @@ function setupStatefulComponent(
     currentSuspense = null
 
     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.
         // bail here and wait for re-entry.
         instance.asyncDep = setupResult
@@ -350,8 +354,6 @@ function setupStatefulComponent(
   } else {
     finishComponentSetup(instance, parentSuspense)
   }
-
-  return setupResult
 }
 
 export function handleSetupResult(
@@ -371,7 +373,7 @@ export function handleSetupResult(
     }
     // setup returned bindings.
     // assuming a render function compiled from template is present.
-    instance.renderContext = reactive(setupResult)
+    instance.renderContext = __SSR__ ? setupResult : reactive(setupResult)
   } else if (__DEV__ && setupResult !== undefined) {
     warn(
       `setup() should return an object. Received: ${
@@ -449,7 +451,7 @@ function finishComponentSetup(
   }
 
   if (instance.renderContext === EMPTY_OBJ) {
-    instance.renderContext = reactive({})
+    instance.renderContext = __SSR__ ? {} : reactive({})
   }
 }