From 532b5eebd7b30d628972a89f16287ca25b9bf7e5 Mon Sep 17 00:00:00 2001 From: likui <2218301630@qq.com> Date: Fri, 25 Oct 2019 22:25:52 +0800 Subject: [PATCH] feat(runtime-core): support array in watch option (#376) --- .../runtime-core/__tests__/apiOptions.spec.ts | 69 +++++++++++++++++++ packages/runtime-core/src/apiOptions.ts | 58 ++++++++++------ 2 files changed, 105 insertions(+), 22 deletions(-) diff --git a/packages/runtime-core/__tests__/apiOptions.spec.ts b/packages/runtime-core/__tests__/apiOptions.spec.ts index c06e062d..a63d3a66 100644 --- a/packages/runtime-core/__tests__/apiOptions.spec.ts +++ b/packages/runtime-core/__tests__/apiOptions.spec.ts @@ -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() { diff --git a/packages/runtime-core/src/apiOptions.ts b/packages/runtime-core/src/apiOptions.ts index 26d1ceac..b8df2fde 100644 --- a/packages/runtime-core/src/apiOptions.ts +++ b/packages/runtime-core/src/apiOptions.ts @@ -119,10 +119,14 @@ export type ExtractComputedReturns = { : ReturnType } -type ComponentWatchOptions = Record< - string, - string | WatchHandler | { handler: WatchHandler } & WatchOptions -> +type WatchOptionItem = + | string + | WatchHandler + | { handler: WatchHandler } & WatchOptions + +type ComponentWatchOptionItem = WatchOptionItem | WatchOptionItem[] + +type ComponentWatchOptions = Record type ComponentInjectOptions = | string[] @@ -148,7 +152,6 @@ export interface LegacyOptions< data?: D | ((this: ComponentPublicInstance) => 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}"`) + } +}