mirror of https://github.com/vuejs/core.git
feat(runtime-vapor): reset old props when setting dynamic props (#131)
Co-authored-by: 三咲智子 Kevin Deng <sxzz@sxzz.moe>
This commit is contained in:
parent
1710bfdd21
commit
b11ecbda69
|
|
@ -5,6 +5,7 @@ import {
|
||||||
setAttr,
|
setAttr,
|
||||||
setClass,
|
setClass,
|
||||||
setDOMProp,
|
setDOMProp,
|
||||||
|
setDynamicProps,
|
||||||
setHtml,
|
setHtml,
|
||||||
setStyle,
|
setStyle,
|
||||||
setText,
|
setText,
|
||||||
|
|
@ -414,6 +415,54 @@ describe('patchProp', () => {
|
||||||
test.todo('should be able to set something on SVG')
|
test.todo('should be able to set something on SVG')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('setDynamicProps', () => {
|
||||||
|
test('basic set dynamic props', () => {
|
||||||
|
const el = document.createElement('div')
|
||||||
|
setDynamicProps(el, { foo: 'val' }, { bar: 'val' })
|
||||||
|
expect(el.getAttribute('foo')).toBe('val')
|
||||||
|
expect(el.getAttribute('bar')).toBe('val')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should merge props', () => {
|
||||||
|
const el = document.createElement('div')
|
||||||
|
setDynamicProps(el, { foo: 'val' }, { foo: 'newVal' })
|
||||||
|
expect(el.getAttribute('foo')).toBe('newVal')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should reset old props', () => {
|
||||||
|
const el = document.createElement('div')
|
||||||
|
|
||||||
|
setDynamicProps(el, { foo: 'val' })
|
||||||
|
expect(el.attributes.length).toBe(1)
|
||||||
|
expect(el.getAttribute('foo')).toBe('val')
|
||||||
|
|
||||||
|
setDynamicProps(el, { bar: 'val' })
|
||||||
|
expect(el.attributes.length).toBe(1)
|
||||||
|
expect(el.getAttribute('bar')).toBe('val')
|
||||||
|
expect(el.getAttribute('foo')).toBeNull()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('should reset old modifier props', () => {
|
||||||
|
const el = document.createElement('div')
|
||||||
|
|
||||||
|
setDynamicProps(el, { ['.foo']: 'val' })
|
||||||
|
expect((el as any).foo).toBe('val')
|
||||||
|
|
||||||
|
setDynamicProps(el, { ['.bar']: 'val' })
|
||||||
|
expect((el as any).bar).toBe('val')
|
||||||
|
expect((el as any).foo).toBe('')
|
||||||
|
|
||||||
|
setDynamicProps(el, { ['^foo']: 'val' })
|
||||||
|
expect(el.attributes.length).toBe(1)
|
||||||
|
expect(el.getAttribute('foo')).toBe('val')
|
||||||
|
|
||||||
|
setDynamicProps(el, { ['^bar']: 'val' })
|
||||||
|
expect(el.attributes.length).toBe(1)
|
||||||
|
expect(el.getAttribute('bar')).toBe('val')
|
||||||
|
expect(el.getAttribute('foo')).toBeNull()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('setText', () => {
|
describe('setText', () => {
|
||||||
test('should set textContent', () => {
|
test('should set textContent', () => {
|
||||||
const el = document.createElement('div')
|
const el = document.createElement('div')
|
||||||
|
|
|
||||||
|
|
@ -9,20 +9,28 @@ import {
|
||||||
normalizeStyle,
|
normalizeStyle,
|
||||||
toDisplayString,
|
toDisplayString,
|
||||||
} from '@vue/shared'
|
} from '@vue/shared'
|
||||||
import { currentInstance } from '../component'
|
import { type ElementMetadata, currentInstance } from '../component'
|
||||||
import { warn } from '../warning'
|
import { warn } from '../warning'
|
||||||
import { setStyle } from './style'
|
import { setStyle } from './style'
|
||||||
|
|
||||||
export function recordPropMetadata(el: Node, key: string, value: any): any {
|
function getMetadata(el: Node): ElementMetadata {
|
||||||
|
const EMPTY_METADATA = { props: {} }
|
||||||
|
|
||||||
if (!currentInstance) {
|
if (!currentInstance) {
|
||||||
// TODO implement error handling
|
// TODO implement error handling
|
||||||
if (__DEV__) throw new Error('cannot be used out of component')
|
if (__DEV__) throw new Error('cannot be used out of component')
|
||||||
return
|
return EMPTY_METADATA
|
||||||
}
|
}
|
||||||
|
|
||||||
let metadata = currentInstance.metadata.get(el)
|
let metadata = currentInstance.metadata.get(el)
|
||||||
if (!metadata) {
|
if (!metadata) {
|
||||||
currentInstance.metadata.set(el, (metadata = { props: {} }))
|
currentInstance.metadata.set(el, (metadata = EMPTY_METADATA))
|
||||||
}
|
}
|
||||||
|
return metadata
|
||||||
|
}
|
||||||
|
|
||||||
|
export function recordPropMetadata(el: Node, key: string, value: any): any {
|
||||||
|
const metadata = getMetadata(el)
|
||||||
const prev = metadata.props[key]
|
const prev = metadata.props[key]
|
||||||
metadata.props[key] = value
|
metadata.props[key] = value
|
||||||
return prev
|
return prev
|
||||||
|
|
@ -140,9 +148,21 @@ export function setDynamicProp(el: Element, key: string, value: any) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setDynamicProps(el: Element, ...args: any) {
|
export function setDynamicProps(el: Element, ...args: any) {
|
||||||
|
const oldProps = getMetadata(el).props
|
||||||
const props = args.length > 1 ? mergeProps(...args) : args[0]
|
const props = args.length > 1 ? mergeProps(...args) : args[0]
|
||||||
|
|
||||||
// TODO remove all of old props before set new props since there is containing dynamic key
|
for (const key in oldProps) {
|
||||||
|
// TODO should these keys be allowed as dynamic keys? The current logic of the runtime-core will throw an error
|
||||||
|
if (key === 'textContent' || key === 'innerHTML') {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const hasNewValue = props[key] || props['.' + key] || props['^' + key]
|
||||||
|
if (oldProps[key] && !hasNewValue) {
|
||||||
|
setDynamicProp(el, key, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (const key in props) {
|
for (const key in props) {
|
||||||
setDynamicProp(el, key, props[key])
|
setDynamicProp(el, key, props[key])
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ const handleClick = () => {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<button @click="handleClick">{{ count }}</button>
|
<button @click="handleClick">click me to update props</button>
|
||||||
|
|
||||||
<!-- prop id's value should update reactively -->
|
<!-- prop id's value should update reactively -->
|
||||||
<button :id="'before'" :[key]="'dynamic key after' + count">
|
<button :id="'before'" :[key]="'dynamic key after' + count">
|
||||||
|
|
@ -34,4 +34,7 @@ const handleClick = () => {
|
||||||
<!-- prop id's value should update only once since the prop id in object props was override -->
|
<!-- prop id's value should update only once since the prop id in object props was override -->
|
||||||
<button v-bind="obj" :id="'after'">{{ count }}</button>
|
<button v-bind="obj" :id="'after'">{{ count }}</button>
|
||||||
<button v-bind="obj" :[key]="'dynamic key after'">{{ count }}</button>
|
<button v-bind="obj" :[key]="'dynamic key after'">{{ count }}</button>
|
||||||
|
|
||||||
|
<!-- old props will be reset after dynamic key changed -->
|
||||||
|
<button :[`key${count}`]="'dynamic key'">{{ count }}</button>
|
||||||
</template>
|
</template>
|
||||||
Loading…
Reference in New Issue