From: skirtle <65301168+skirtles-code@users.noreply.github.com> Date: Thu, 30 May 2024 03:03:44 +0000 (+0100) Subject: fix(watch): support traversing symbol properties in deep watcher (#10969) X-Git-Tag: v3.4.28~61 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a3e8aafbcc82003a66caded61143eb64c4ef02cd;p=thirdparty%2Fvuejs%2Fcore.git fix(watch): support traversing symbol properties in deep watcher (#10969) close #402 --- diff --git a/packages/runtime-core/__tests__/apiWatch.spec.ts b/packages/runtime-core/__tests__/apiWatch.spec.ts index 8229dce36d..265bc0a0d4 100644 --- a/packages/runtime-core/__tests__/apiWatch.spec.ts +++ b/packages/runtime-core/__tests__/apiWatch.spec.ts @@ -932,6 +932,52 @@ describe('api: watch', () => { expect(dummy).toEqual([1, 2]) }) + it('deep with symbols', async () => { + const symbol1 = Symbol() + const symbol2 = Symbol() + const symbol3 = Symbol() + const symbol4 = Symbol() + + const raw: any = { + [symbol1]: { + [symbol2]: 1, + }, + } + + Object.defineProperty(raw, symbol3, { + writable: true, + enumerable: false, + value: 1, + }) + + const state = reactive(raw) + const spy = vi.fn() + + watch(() => state, spy, { deep: true }) + + await nextTick() + expect(spy).toHaveBeenCalledTimes(0) + + state[symbol1][symbol2] = 2 + await nextTick() + expect(spy).toHaveBeenCalledTimes(1) + + // Non-enumerable properties don't trigger deep watchers + state[symbol3] = 3 + await nextTick() + expect(spy).toHaveBeenCalledTimes(1) + + // Adding a new symbol property + state[symbol4] = 1 + await nextTick() + expect(spy).toHaveBeenCalledTimes(2) + + // Removing a symbol property + delete state[symbol4] + await nextTick() + expect(spy).toHaveBeenCalledTimes(3) + }) + it('immediate', async () => { const count = ref(0) const cb = vi.fn() diff --git a/packages/runtime-core/src/apiWatch.ts b/packages/runtime-core/src/apiWatch.ts index ffad8ad549..bab9e0764f 100644 --- a/packages/runtime-core/src/apiWatch.ts +++ b/packages/runtime-core/src/apiWatch.ts @@ -493,6 +493,11 @@ export function traverse( for (const key in value) { traverse(value[key], depth, seen) } + for (const key of Object.getOwnPropertySymbols(value)) { + if (Object.prototype.propertyIsEnumerable.call(value, key)) { + traverse(value[key as any], depth, seen) + } + } } return value }