feat(runtime-core): support array in watch option (#376)

This commit is contained in:
likui 2019-10-25 22:25:52 +08:00 committed by Evan You
parent a75077569e
commit 532b5eebd7
2 changed files with 105 additions and 22 deletions

View 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() {

View 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}"`)
}
}