Skip to content

Commit b11ecbd

Browse files
ygj6sxzz
andauthored
feat(runtime-vapor): reset old props when setting dynamic props (#131)
Co-authored-by: 三咲智子 Kevin Deng <[email protected]>
1 parent 1710bfd commit b11ecbd

File tree

3 files changed

+78
-6
lines changed

3 files changed

+78
-6
lines changed

packages/runtime-vapor/__tests__/dom/patchProp.spec.ts

+49
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
setAttr,
66
setClass,
77
setDOMProp,
8+
setDynamicProps,
89
setHtml,
910
setStyle,
1011
setText,
@@ -414,6 +415,54 @@ describe('patchProp', () => {
414415
test.todo('should be able to set something on SVG')
415416
})
416417

418+
describe('setDynamicProps', () => {
419+
test('basic set dynamic props', () => {
420+
const el = document.createElement('div')
421+
setDynamicProps(el, { foo: 'val' }, { bar: 'val' })
422+
expect(el.getAttribute('foo')).toBe('val')
423+
expect(el.getAttribute('bar')).toBe('val')
424+
})
425+
426+
test('should merge props', () => {
427+
const el = document.createElement('div')
428+
setDynamicProps(el, { foo: 'val' }, { foo: 'newVal' })
429+
expect(el.getAttribute('foo')).toBe('newVal')
430+
})
431+
432+
test('should reset old props', () => {
433+
const el = document.createElement('div')
434+
435+
setDynamicProps(el, { foo: 'val' })
436+
expect(el.attributes.length).toBe(1)
437+
expect(el.getAttribute('foo')).toBe('val')
438+
439+
setDynamicProps(el, { bar: 'val' })
440+
expect(el.attributes.length).toBe(1)
441+
expect(el.getAttribute('bar')).toBe('val')
442+
expect(el.getAttribute('foo')).toBeNull()
443+
})
444+
445+
test('should reset old modifier props', () => {
446+
const el = document.createElement('div')
447+
448+
setDynamicProps(el, { ['.foo']: 'val' })
449+
expect((el as any).foo).toBe('val')
450+
451+
setDynamicProps(el, { ['.bar']: 'val' })
452+
expect((el as any).bar).toBe('val')
453+
expect((el as any).foo).toBe('')
454+
455+
setDynamicProps(el, { ['^foo']: 'val' })
456+
expect(el.attributes.length).toBe(1)
457+
expect(el.getAttribute('foo')).toBe('val')
458+
459+
setDynamicProps(el, { ['^bar']: 'val' })
460+
expect(el.attributes.length).toBe(1)
461+
expect(el.getAttribute('bar')).toBe('val')
462+
expect(el.getAttribute('foo')).toBeNull()
463+
})
464+
})
465+
417466
describe('setText', () => {
418467
test('should set textContent', () => {
419468
const el = document.createElement('div')

packages/runtime-vapor/src/dom/prop.ts

+25-5
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,28 @@ import {
99
normalizeStyle,
1010
toDisplayString,
1111
} from '@vue/shared'
12-
import { currentInstance } from '../component'
12+
import { type ElementMetadata, currentInstance } from '../component'
1313
import { warn } from '../warning'
1414
import { setStyle } from './style'
1515

16-
export function recordPropMetadata(el: Node, key: string, value: any): any {
16+
function getMetadata(el: Node): ElementMetadata {
17+
const EMPTY_METADATA = { props: {} }
18+
1719
if (!currentInstance) {
1820
// TODO implement error handling
1921
if (__DEV__) throw new Error('cannot be used out of component')
20-
return
22+
return EMPTY_METADATA
2123
}
24+
2225
let metadata = currentInstance.metadata.get(el)
2326
if (!metadata) {
24-
currentInstance.metadata.set(el, (metadata = { props: {} }))
27+
currentInstance.metadata.set(el, (metadata = EMPTY_METADATA))
2528
}
29+
return metadata
30+
}
31+
32+
export function recordPropMetadata(el: Node, key: string, value: any): any {
33+
const metadata = getMetadata(el)
2634
const prev = metadata.props[key]
2735
metadata.props[key] = value
2836
return prev
@@ -140,9 +148,21 @@ export function setDynamicProp(el: Element, key: string, value: any) {
140148
}
141149

142150
export function setDynamicProps(el: Element, ...args: any) {
151+
const oldProps = getMetadata(el).props
143152
const props = args.length > 1 ? mergeProps(...args) : args[0]
144153

145-
// TODO remove all of old props before set new props since there is containing dynamic key
154+
for (const key in oldProps) {
155+
// TODO should these keys be allowed as dynamic keys? The current logic of the runtime-core will throw an error
156+
if (key === 'textContent' || key === 'innerHTML') {
157+
continue
158+
}
159+
160+
const hasNewValue = props[key] || props['.' + key] || props['^' + key]
161+
if (oldProps[key] && !hasNewValue) {
162+
setDynamicProp(el, key, null)
163+
}
164+
}
165+
146166
for (const key in props) {
147167
setDynamicProp(el, key, props[key])
148168
}

playground/src/v-bind.vue renamed to playground/src/prop.vue

+4-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const handleClick = () => {
1111
</script>
1212

1313
<template>
14-
<button @click="handleClick">{{ count }}</button>
14+
<button @click="handleClick">click me to update props</button>
1515

1616
<!-- prop id's value should update reactively -->
1717
<button :id="'before'" :[key]="'dynamic key after' + count">
@@ -34,4 +34,7 @@ const handleClick = () => {
3434
<!-- prop id's value should update only once since the prop id in object props was override -->
3535
<button v-bind="obj" :id="'after'">{{ count }}</button>
3636
<button v-bind="obj" :[key]="'dynamic key after'">{{ count }}</button>
37+
38+
<!-- old props will be reset after dynamic key changed -->
39+
<button :[`key${count}`]="'dynamic key'">{{ count }}</button>
3740
</template>

0 commit comments

Comments
 (0)