fix(watch): this.$watch should support watching keypath

This commit is contained in:
Evan You 2021-04-07 16:19:04 -04:00
parent 0f2d8f3244
commit 870f2a7ba3
3 changed files with 49 additions and 13 deletions

View File

@ -915,4 +915,33 @@ describe('api: watch', () => {
// should not track b as dependency of Child // should not track b as dependency of Child
expect(updated).toHaveBeenCalledTimes(1) expect(updated).toHaveBeenCalledTimes(1)
}) })
test('watching keypath', async () => {
const spy = jest.fn()
const Comp = defineComponent({
render() {},
data() {
return {
a: {
b: 1
}
}
},
watch: {
'a.b': spy
},
created(this: any) {
this.$watch('a.b', spy)
},
mounted(this: any) {
this.a.b++
}
})
const root = nodeOps.createElement('div')
createApp(Comp).mount(root)
await nextTick()
expect(spy).toHaveBeenCalledTimes(2)
})
}) })

View File

@ -334,11 +334,24 @@ export function instanceWatch(
): WatchStopHandle { ): WatchStopHandle {
const publicThis = this.proxy as any const publicThis = this.proxy as any
const getter = isString(source) const getter = isString(source)
? () => publicThis[source] ? source.includes('.')
? createPathGetter(publicThis, source)
: () => publicThis[source]
: source.bind(publicThis) : source.bind(publicThis)
return doWatch(getter, cb.bind(publicThis), options, this) return doWatch(getter, cb.bind(publicThis), options, this)
} }
export function createPathGetter(ctx: any, path: string) {
const segments = path.split('.')
return () => {
let cur = ctx
for (let i = 0; i < segments.length && cur; i++) {
cur = cur[segments[i]]
}
return cur
}
}
function traverse(value: unknown, seen: Set<unknown> = new Set()) { function traverse(value: unknown, seen: Set<unknown> = new Set()) {
if (!isObject(value) || seen.has(value)) { if (!isObject(value) || seen.has(value)) {
return value return value

View File

@ -20,7 +20,12 @@ import {
isPromise isPromise
} from '@vue/shared' } from '@vue/shared'
import { computed } from './apiComputed' import { computed } from './apiComputed'
import { watch, WatchOptions, WatchCallback } from './apiWatch' import {
watch,
WatchOptions,
WatchCallback,
createPathGetter
} from './apiWatch'
import { provide, inject } from './apiInject' import { provide, inject } from './apiInject'
import { import {
onBeforeMount, onBeforeMount,
@ -939,17 +944,6 @@ function createWatcher(
} }
} }
function createPathGetter(ctx: any, path: string) {
const segments = path.split('.')
return () => {
let cur = ctx
for (let i = 0; i < segments.length && cur; i++) {
cur = cur[segments[i]]
}
return cur
}
}
export function resolveMergedOptions( export function resolveMergedOptions(
instance: ComponentInternalInstance instance: ComponentInternalInstance
): ComponentOptions { ): ComponentOptions {