]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(watch): support directly watching reactive object with deep default
authorEvan You <yyx990803@gmail.com>
Mon, 4 May 2020 13:27:28 +0000 (09:27 -0400)
committerEvan You <yyx990803@gmail.com>
Mon, 4 May 2020 13:27:28 +0000 (09:27 -0400)
Also warn invalid watch sources

close #1110

packages/runtime-core/__tests__/apiWatch.spec.ts
packages/runtime-core/src/apiWatch.ts

index 8b58ded39037fc5fe3821346f62205dac043d6e0..71dfd3ca0148922a9601a1b683daa3c220d88f86 100644 (file)
@@ -103,6 +103,19 @@ describe('api: watch', () => {
     expect(dummy).toMatchObject([1, 0])
   })
 
+  it('directly watching reactive object (with automatic deep: true)', async () => {
+    const src = reactive({
+      count: 0
+    })
+    let dummy
+    watch(src, ({ count }) => {
+      dummy = count
+    })
+    src.count++
+    await nextTick()
+    expect(dummy).toBe(1)
+  })
+
   it('watching multiple sources', async () => {
     const state = reactive({ count: 1 })
     const count = ref(1)
@@ -142,6 +155,12 @@ describe('api: watch', () => {
     expect(dummy).toMatchObject([[2, true], [1, false]])
   })
 
+  it('warn invalid watch source', () => {
+    // @ts-ignore
+    watch(1, () => {})
+    expect(`Invalid watch source`).toHaveBeenWarned()
+  })
+
   it('stopping the watcher (effect)', async () => {
     const state = reactive({ count: 0 })
     let dummy
index 13e5ab027748593c7eeb4cbfda8a610d5933dc36..322e38e6cf8f814e2931b3a90d3d1ecbb3af8f87 100644 (file)
@@ -4,7 +4,8 @@ import {
   isRef,
   Ref,
   ComputedRef,
-  ReactiveEffectOptions
+  ReactiveEffectOptions,
+  isReactive
 } from '@vue/reactivity'
 import { queueJob } from './scheduler'
 import {
@@ -80,14 +81,7 @@ export function watchEffect(
 // initial value for watchers to trigger on undefined initial values
 const INITIAL_WATCHER_VALUE = {}
 
-// overload #1: single source + cb
-export function watch<T, Immediate extends Readonly<boolean> = false>(
-  source: WatchSource<T>,
-  cb: WatchCallback<T, Immediate extends true ? (T | undefined) : T>,
-  options?: WatchOptions<Immediate>
-): WatchStopHandle
-
-// overload #2: array of multiple sources + cb
+// overload #1: array of multiple sources + cb
 // Readonly constraint helps the callback to correctly infer value types based
 // on position in the source array. Otherwise the values will get a union type
 // of all possible value types.
@@ -100,6 +94,23 @@ export function watch<
   options?: WatchOptions<Immediate>
 ): WatchStopHandle
 
+// overload #2: single source + cb
+export function watch<T, Immediate extends Readonly<boolean> = false>(
+  source: WatchSource<T>,
+  cb: WatchCallback<T, Immediate extends true ? (T | undefined) : T>,
+  options?: WatchOptions<Immediate>
+): WatchStopHandle
+
+// overload #3: watching reactive object w/ cb
+export function watch<
+  T extends object,
+  Immediate extends Readonly<boolean> = false
+>(
+  source: T,
+  cb: WatchCallback<T, Immediate extends true ? (T | undefined) : T>,
+  options?: WatchOptions<Immediate>
+): WatchStopHandle
+
 // implementation
 export function watch<T = any>(
   source: WatchSource<T> | WatchSource<T>[],
@@ -149,26 +160,39 @@ function doWatch(
       )
   } else if (isRef(source)) {
     getter = () => source.value
-  } else if (cb) {
-    // getter with cb
-    getter = () =>
-      callWithErrorHandling(source, instance, ErrorCodes.WATCH_GETTER)
-  } else {
-    // no cb -> simple effect
-    getter = () => {
-      if (instance && instance.isUnmounted) {
-        return
-      }
-      if (cleanup) {
-        cleanup()
+  } else if (isReactive(source)) {
+    getter = () => source
+    deep = true
+  } else if (isFunction(source)) {
+    if (cb) {
+      // getter with cb
+      getter = () =>
+        callWithErrorHandling(source, instance, ErrorCodes.WATCH_GETTER)
+    } else {
+      // no cb -> simple effect
+      getter = () => {
+        if (instance && instance.isUnmounted) {
+          return
+        }
+        if (cleanup) {
+          cleanup()
+        }
+        return callWithErrorHandling(
+          source,
+          instance,
+          ErrorCodes.WATCH_CALLBACK,
+          [onInvalidate]
+        )
       }
-      return callWithErrorHandling(
-        source,
-        instance,
-        ErrorCodes.WATCH_CALLBACK,
-        [onInvalidate]
-      )
     }
+  } else {
+    getter = NOOP
+    warn(
+      `Invalid watch source: `,
+      source,
+      `A watch source can only be a getter/effect function, a ref, ` +
+        `a reactive object, or an array of these types.`
+    )
   }
 
   if (cb && deep) {