diff --git a/packages/runtime-core/__tests__/apiWatch.spec.ts b/packages/runtime-core/__tests__/apiWatch.spec.ts index 07a589fb0f5..484fed4745c 100644 --- a/packages/runtime-core/__tests__/apiWatch.spec.ts +++ b/packages/runtime-core/__tests__/apiWatch.spec.ts @@ -1446,6 +1446,33 @@ describe('api: watch', () => { expect(spy).toHaveBeenCalledTimes(2) }) + test('watching keypath should not stop on falsy intermediate values', async () => { + const spy = vi.fn() + const Comp = defineComponent({ + render() {}, + data() { + return { + a: { + b: 0 as any, + }, + } + }, + created(this: any) { + this.$watch('a.b', spy) + }, + mounted(this: any) { + this.a.b = 1 + }, + }) + + const root = nodeOps.createElement('div') + createApp(Comp).mount(root) + + await nextTick() + expect(spy).toHaveBeenCalledTimes(1) + expect(spy).toHaveBeenCalledWith(1, 0, expect.anything()) + }) + it('watching sources: ref', async () => { const foo = ref([1]) const spy = vi.fn() diff --git a/packages/runtime-core/src/apiWatch.ts b/packages/runtime-core/src/apiWatch.ts index 4540ba1b03a..e6d0a3795df 100644 --- a/packages/runtime-core/src/apiWatch.ts +++ b/packages/runtime-core/src/apiWatch.ts @@ -274,7 +274,7 @@ export function createPathGetter( const segments = path.split('.') return (): WatchSource | WatchSource[] | WatchEffect | object => { let cur = ctx - for (let i = 0; i < segments.length && cur; i++) { + for (let i = 0; i < segments.length && cur != null; i++) { cur = cur[segments[i] as keyof typeof cur] } return cur diff --git a/packages/runtime-dom/__tests__/patchStyle.spec.ts b/packages/runtime-dom/__tests__/patchStyle.spec.ts index 8b2765e2149..56960751e1a 100644 --- a/packages/runtime-dom/__tests__/patchStyle.spec.ts +++ b/packages/runtime-dom/__tests__/patchStyle.spec.ts @@ -167,4 +167,15 @@ describe(`runtime-dom: style patching`, () => { patchProp(el, 'style', 'color:red', { fontSize: '12px' }) expect(el.style.cssText.replace(/\s/g, '')).toBe('font-size:12px;') }) + + it('should handle string style with trailing semicolon when patching to object', () => { + const el = document.createElement('div') + // set initial string style with trailing semicolon + patchProp(el, 'style', {}, 'color:red;') + expect(el.style.cssText.replace(/\s/g, '')).toBe('color:red;') + + // patch from string to object, entries without colon (from trailing ;) should be skipped + patchProp(el, 'style', 'color:red;', { fontSize: '12px' }) + expect(el.style.cssText.replace(/\s/g, '')).toBe('font-size:12px;') + }) }) diff --git a/packages/runtime-dom/src/modules/style.ts b/packages/runtime-dom/src/modules/style.ts index 679064bd709..48ab9d6e3cb 100644 --- a/packages/runtime-dom/src/modules/style.ts +++ b/packages/runtime-dom/src/modules/style.ts @@ -25,7 +25,9 @@ export function patchStyle(el: Element, prev: Style, next: Style): void { } } else { for (const prevStyle of prev.split(';')) { - const key = prevStyle.slice(0, prevStyle.indexOf(':')).trim() + const colonIndex = prevStyle.indexOf(':') + if (colonIndex < 0) continue + const key = prevStyle.slice(0, colonIndex).trim() if (next[key] == null) { setStyle(style, key, '') }