]> git.ipfire.org Git - thirdparty/vuejs/core.git/commitdiff
feat(runtime-core): support array in watch option (#376)
authorlikui <2218301630@qq.com>
Fri, 25 Oct 2019 14:25:52 +0000 (22:25 +0800)
committerEvan You <yyx990803@gmail.com>
Fri, 25 Oct 2019 14:25:52 +0000 (10:25 -0400)
packages/runtime-core/__tests__/apiOptions.spec.ts
packages/runtime-core/src/apiOptions.ts

index c06e062db1e812c6ed93d6d5fd20a9a8ed794416..a63d3a66fd3363132a45088a17c3364174918e67 100644 (file)
@@ -175,6 +175,75 @@ describe('api: options', () => {
     assertCall(spyC, 1, [{ qux: 4 }, { qux: 4 }])
   })
 
+  test('watch array', async () => {
+    function returnThis(this: any) {
+      return this
+    }
+    const spyA = jest.fn(returnThis)
+    const spyB = jest.fn(returnThis)
+    const spyC = jest.fn(returnThis)
+
+    let ctx: any
+    const Comp = {
+      data() {
+        return {
+          foo: 1,
+          bar: 2,
+          baz: {
+            qux: 3
+          }
+        }
+      },
+      watch: {
+        // string method name
+        foo: ['onFooChange'],
+        // direct function
+        bar: [spyB],
+        baz: [
+          {
+            handler: spyC,
+            deep: true
+          }
+        ]
+      },
+      methods: {
+        onFooChange: spyA
+      },
+      render() {
+        ctx = this
+      }
+    }
+    const root = nodeOps.createElement('div')
+    render(h(Comp), root)
+
+    function assertCall(spy: jest.Mock, callIndex: number, args: any[]) {
+      expect(spy.mock.calls[callIndex].slice(0, 2)).toMatchObject(args)
+    }
+
+    assertCall(spyA, 0, [1, undefined])
+    assertCall(spyB, 0, [2, undefined])
+    assertCall(spyC, 0, [{ qux: 3 }, undefined])
+    expect(spyA).toHaveReturnedWith(ctx)
+    expect(spyB).toHaveReturnedWith(ctx)
+    expect(spyC).toHaveReturnedWith(ctx)
+
+    ctx.foo++
+    await nextTick()
+    expect(spyA).toHaveBeenCalledTimes(2)
+    assertCall(spyA, 1, [2, 1])
+
+    ctx.bar++
+    await nextTick()
+    expect(spyB).toHaveBeenCalledTimes(2)
+    assertCall(spyB, 1, [3, 2])
+
+    ctx.baz.qux++
+    await nextTick()
+    expect(spyC).toHaveBeenCalledTimes(2)
+    // new and old objects have same identity
+    assertCall(spyC, 1, [{ qux: 4 }, { qux: 4 }])
+  })
+
   test('provide/inject', () => {
     const Root = {
       data() {
index 26d1ceac59926fcc04049b0f6277db9ecd16a0fd..b8df2fdea442f6144ce907fdd3ed05d069ec2722 100644 (file)
@@ -119,10 +119,14 @@ export type ExtractComputedReturns<T extends any> = {
     : ReturnType<T[key]>
 }
 
-type ComponentWatchOptions = Record<
-  string,
-  string | WatchHandler | { handler: WatchHandler } & WatchOptions
->
+type WatchOptionItem =
+  | string
+  | WatchHandler
+  | { handler: WatchHandler } & WatchOptions
+
+type ComponentWatchOptionItem = WatchOptionItem | WatchOptionItem[]
+
+type ComponentWatchOptions = Record<string, ComponentWatchOptionItem>
 
 type ComponentInjectOptions =
   | string[]
@@ -148,7 +152,6 @@ export interface LegacyOptions<
   data?: D | ((this: ComponentPublicInstance<Props>) => D)
   computed?: C
   methods?: M
-  // TODO watch array
   watch?: ComponentWatchOptions
   provide?: Data | Function
   inject?: ComponentInjectOptions
@@ -318,23 +321,7 @@ export function applyOptions(
   }
   if (watchOptions) {
     for (const key in watchOptions) {
-      const raw = watchOptions[key]
-      const getter = () => ctx[key]
-      if (isString(raw)) {
-        const handler = renderContext[raw]
-        if (isFunction(handler)) {
-          watch(getter, handler as WatchHandler)
-        } else if (__DEV__) {
-          warn(`Invalid watch handler specified by key "${raw}"`, handler)
-        }
-      } else if (isFunction(raw)) {
-        watch(getter, raw.bind(ctx))
-      } else if (isObject(raw)) {
-        // TODO 2.x compat
-        watch(getter, raw.handler.bind(ctx), raw)
-      } else if (__DEV__) {
-        warn(`Invalid watch option: "${key}"`)
-      }
+      createWatcher(watchOptions[key], renderContext, ctx, key)
     }
   }
   if (provideOptions) {
@@ -448,3 +435,30 @@ function applyMixins(
     applyOptions(instance, mixins[i], true)
   }
 }
+
+function createWatcher(
+  raw: ComponentWatchOptionItem,
+  renderContext: Data,
+  ctx: ComponentPublicInstance,
+  key: string
+) {
+  const getter = () => ctx[key]
+  if (isString(raw)) {
+    const handler = renderContext[raw]
+    if (isFunction(handler)) {
+      watch(getter, handler as WatchHandler)
+    } else if (__DEV__) {
+      warn(`Invalid watch handler specified by key "${raw}"`, handler)
+    }
+  } else if (isFunction(raw)) {
+    watch(getter, raw.bind(ctx))
+  } else if (isObject(raw)) {
+    if (isArray(raw)) {
+      raw.forEach(r => createWatcher(r, renderContext, ctx, key))
+    } else {
+      watch(getter, raw.handler.bind(ctx), raw)
+    }
+  } else if (__DEV__) {
+    warn(`Invalid watch option: "${key}"`)
+  }
+}