]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(watch): support directly watching reactive object in multiple sources with deep...
authorYang Mingshan <y.mingshan3@gmail.com>
Mon, 18 May 2020 15:02:51 +0000 (23:02 +0800)
committerGitHub <noreply@github.com>
Mon, 18 May 2020 15:02:51 +0000 (11:02 -0400)
packages/runtime-core/__tests__/apiWatch.spec.ts
packages/runtime-core/src/apiWatch.ts

index 71dfd3ca0148922a9601a1b683daa3c220d88f86..0abf009af007e73970242e8f3704340704293bcb 100644 (file)
@@ -155,12 +155,30 @@ describe('api: watch', () => {
     expect(dummy).toMatchObject([[2, true], [1, false]])
   })
 
+  it('watching multiple sources: reactive object (with automatic deep: true)', async () => {
+    const src = reactive({ count: 0 })
+    let dummy
+    watch([src], ([state]) => {
+      dummy = state
+      // assert types
+      state.count === 1
+    })
+    src.count++
+    await nextTick()
+    expect(dummy).toMatchObject({ count: 1 })
+  })
+
   it('warn invalid watch source', () => {
     // @ts-ignore
     watch(1, () => {})
     expect(`Invalid watch source`).toHaveBeenWarned()
   })
 
+  it('warn invalid watch source: multiple sources', () => {
+    watch([1], () => {})
+    expect(`Invalid watch source`).toHaveBeenWarned()
+  })
+
   it('stopping the watcher (effect)', async () => {
     const state = reactive({ count: 0 })
     let dummy
index 4fcc0a850b57640b43f4afa4085f03a272a9a253..4d98d425a8b403059cb8e9ea3e405ecb21a1cc91 100644 (file)
@@ -44,13 +44,17 @@ export type WatchCallback<V = any, OV = any> = (
 ) => any
 
 type MapSources<T> = {
-  [K in keyof T]: T[K] extends WatchSource<infer V> ? V : never
+  [K in keyof T]: T[K] extends WatchSource<infer V>
+    ? V
+    : T[K] extends object ? T[K] : never
 }
 
 type MapOldSources<T, Immediate> = {
   [K in keyof T]: T[K] extends WatchSource<infer V>
     ? Immediate extends true ? (V | undefined) : V
-    : never
+    : T[K] extends object
+      ? Immediate extends true ? (T[K] | undefined) : T[K]
+      : never
 }
 
 type InvalidateCbRegistrator = (cb: () => void) => void
@@ -86,7 +90,7 @@ const INITIAL_WATCHER_VALUE = {}
 // on position in the source array. Otherwise the values will get a union type
 // of all possible value types.
 export function watch<
-  T extends Readonly<WatchSource<unknown>[]>,
+  T extends Readonly<Array<WatchSource<unknown> | object>>,
   Immediate extends Readonly<boolean> = false
 >(
   sources: T,
@@ -147,17 +151,31 @@ function doWatch(
     }
   }
 
+  const warnInvalidSource = (s: unknown) => {
+    warn(
+      `Invalid watch source: `,
+      s,
+      `A watch source can only be a getter/effect function, a ref, ` +
+        `a reactive object, or an array of these types.`
+    )
+  }
+
   const instance = currentInstance
 
   let getter: () => any
   if (isArray(source)) {
     getter = () =>
-      source.map(
-        s =>
-          isRef(s)
-            ? s.value
-            : callWithErrorHandling(s, instance, ErrorCodes.WATCH_GETTER)
-      )
+      source.map(s => {
+        if (isRef(s)) {
+          return s.value
+        } else if (isReactive(s)) {
+          return traverse(s)
+        } else if (isFunction(s)) {
+          return callWithErrorHandling(s, instance, ErrorCodes.WATCH_GETTER)
+        } else {
+          __DEV__ && warnInvalidSource(s)
+        }
+      })
   } else if (isRef(source)) {
     getter = () => source.value
   } else if (isReactive(source)) {
@@ -187,13 +205,7 @@ function doWatch(
     }
   } else {
     getter = NOOP
-    __DEV__ &&
-      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.`
-      )
+    __DEV__ && warnInvalidSource(source)
   }
 
   if (cb && deep) {