Skip to content
This repository was archived by the owner on Jul 19, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,54 @@ export function render(_ctx) {
}"
`;

exports[`compiler v-bind > :innerHTML 1`] = `
"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setHtml as _setHtml, template as _template } from 'vue/vapor';
const t0 = _template("<div></div>")

export function render(_ctx) {
const n0 = t0()
_setInheritAttrs(["innerHTML"])
_renderEffect(() => _setHtml(n0, _ctx.foo, true))
return n0
}"
`;

exports[`compiler v-bind > :textContext 1`] = `
"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setText as _setText, template as _template } from 'vue/vapor';
const t0 = _template("<div></div>")

export function render(_ctx) {
const n0 = t0()
_setInheritAttrs(["textContent"])
_renderEffect(() => _setText(n0, _ctx.foo, true))
return n0
}"
`;

exports[`compiler v-bind > :value 1`] = `
"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setValue as _setValue, template as _template } from 'vue/vapor';
const t0 = _template("<input>")

export function render(_ctx) {
const n0 = t0()
_setInheritAttrs(["value"])
_renderEffect(() => _setValue(n0, _ctx.foo, true))
return n0
}"
`;

exports[`compiler v-bind > :value w/ progress 1`] = `
"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDynamicProp as _setDynamicProp, template as _template } from 'vue/vapor';
const t0 = _template("<progress></progress>")

export function render(_ctx) {
const n0 = t0()
_setInheritAttrs(["value"])
_renderEffect(() => _setDynamicProp(n0, "value", _ctx.foo, true))
return n0
}"
`;

exports[`compiler v-bind > HTML global attributes should set as dom prop 1`] = `
"import { setInheritAttrs as _setInheritAttrs, renderEffect as _renderEffect, setDOMProp as _setDOMProp, template as _template } from 'vue/vapor';
const t0 = _template("<div></div>")
Expand Down
32 changes: 32 additions & 0 deletions packages/compiler-vapor/__tests__/transforms/vBind.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,38 @@ describe('compiler v-bind', () => {
expect(code).contains('_setDOMProp(n0, "tabindex", _ctx.tabindex, true)')
})

test(':innerHTML', () => {
const { code } = compileWithVBind(`
<div :innerHTML="foo"/>
`)
expect(code).matchSnapshot()
expect(code).contains('_setHtml(n0, _ctx.foo, true)')
})

test(':textContext', () => {
const { code } = compileWithVBind(`
<div :textContent="foo"/>
`)
expect(code).matchSnapshot()
expect(code).contains('_setText(n0, _ctx.foo, true)')
})

test(':value', () => {
const { code } = compileWithVBind(`
<input :value="foo"/>
`)
expect(code).matchSnapshot()
expect(code).contains('_setValue(n0, _ctx.foo, true)')
})

test(':value w/ progress', () => {
const { code } = compileWithVBind(`
<progress :value="foo"/>
`)
expect(code).matchSnapshot()
expect(code).contains('_setDynamicProp(n0, "value", _ctx.foo, true)')
})

test('number value', () => {
const { code } = compileWithVBind(`<Comp :depth="0" />`)
expect(code).matchSnapshot()
Expand Down
13 changes: 13 additions & 0 deletions packages/compiler-vapor/src/generators/prop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,19 @@ export function genSetProp(
: attributeCache[attrCacheKey]
) {
helperName = 'setAttr'
} else if (keyName === 'innerHTML') {
helperName = 'setHtml'
omitKey = true
} else if (keyName === 'textContent') {
helperName = 'setText'
omitKey = true
} else if (
keyName === 'value' &&
tagName !== 'PROGRESS' &&
!tagName.includes('-')
) {
helperName = 'setValue'
omitKey = true
} else if (
(isHTMLTag(tag) && isHTMLGlobalAttr(keyName)) ||
(isSVGTag(tag) && isSvgGlobalAttr(keyName)) ||
Expand Down
32 changes: 13 additions & 19 deletions packages/runtime-vapor/__tests__/dom/prop.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
setDynamicProps,
setHtml,
setText,
setValue,
} from '../../src/dom/prop'
import { setStyle } from '../../src/dom/style'
import {
Expand Down Expand Up @@ -239,43 +240,32 @@ describe('patchProp', () => {
})
})

describe('setDOMProp', () => {
test('should set DOM property', () => {
const el = document.createElement('div')
setDOMProp(el, 'textContent', null)
expect(el.textContent).toBe('')
setDOMProp(el, 'textContent', 'foo')
expect(el.textContent).toBe('foo')

setDOMProp(el, 'innerHTML', null)
expect(el.innerHTML).toBe('')
setDOMProp(el, 'innerHTML', '<p>bar</p>')
expect(el.innerHTML).toBe('<p>bar</p>')
})

describe('setValue', () => {
test('should set value prop', () => {
const el = document.createElement('input')
setDOMProp(el, 'value', 'foo')
setValue(el, 'foo')
expect(el.value).toBe('foo')
setDOMProp(el, 'value', null)
setValue(el, null)
expect(el.value).toBe('')
expect(el.getAttribute('value')).toBe(null)
const obj = {}
setDOMProp(el, 'value', obj)
setValue(el, obj)
expect(el.value).toBe(obj.toString())
expect((el as any)._value).toBe(obj)

const option = document.createElement('option')
setDOMProp(option, 'textContent', 'foo')
setText(option, 'foo')
expect(option.value).toBe('foo')
expect(option.getAttribute('value')).toBe(null)

setDOMProp(option, 'value', 'bar')
setValue(option, 'bar')
expect(option.textContent).toBe('foo')
expect(option.value).toBe('bar')
expect(option.getAttribute('value')).toBe('bar')
})
})

describe('setDOMProp', () => {
test('should be boolean prop', () => {
const el = document.createElement('select')
setDOMProp(el, 'multiple', '')
Expand Down Expand Up @@ -455,6 +445,8 @@ describe('patchProp', () => {
describe('setText', () => {
test('should set textContent', () => {
const el = document.createElement('div')
setText(el, null)
expect(el.textContent).toBe('')
setText(el, 'foo')
expect(el.textContent).toBe('foo')
setText(el, 'bar')
Expand All @@ -465,6 +457,8 @@ describe('patchProp', () => {
describe('setHtml', () => {
test('should set innerHTML', () => {
const el = document.createElement('div')
setHtml(el, null)
expect(el.innerHTML).toBe('')
setHtml(el, '<p>foo</p>')
expect(el.innerHTML).toBe('<p>foo</p>')
setHtml(el, '<p>bar</p>')
Expand Down
76 changes: 42 additions & 34 deletions packages/runtime-vapor/src/dom/prop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,42 +50,28 @@ export function setAttr(el: Element, key: string, value: any): void {
}
}

export function setDOMProp(el: any, key: string, value: any): void {
const oldVal = recordPropMetadata(el, key, value)
export function setValue(el: any, value: any): void {
const oldVal = recordPropMetadata(el, 'value', value)
if (value === oldVal) return

if (key === 'innerHTML' || key === 'textContent') {
// TODO special checks
Copy link
Member Author

@edison1105 edison1105 Nov 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this special check is no longer necessary

// if (prevChildren) {
// unmountChildren(prevChildren, parentComponent, parentSuspense)
// }
el[key] = value == null ? '' : value
return
// store value as _value as well since
// non-string values will be stringified.
el._value = value
// #4956: <option> value will fallback to its text content so we need to
// compare against its attribute value instead.
const oldValue = el.tagName === 'OPTION' ? el.getAttribute('value') : el.value
const newValue = value == null ? '' : value
if (oldValue !== newValue) {
el.value = newValue
}

const tag = el.tagName

if (
key === 'value' &&
tag !== 'PROGRESS' &&
// custom elements may use _value internally
!tag.includes('-')
) {
// store value as _value as well since
// non-string values will be stringified.
el._value = value
// #4956: <option> value will fallback to its text content so we need to
// compare against its attribute value instead.
const oldValue = tag === 'OPTION' ? el.getAttribute('value') : el.value
const newValue = value == null ? '' : value
if (oldValue !== newValue) {
el.value = newValue
}
if (value == null) {
el.removeAttribute(key)
}
return
if (value == null) {
el.removeAttribute('value')
}
}

export function setDOMProp(el: any, key: string, value: any): void {
const oldVal = recordPropMetadata(el, key, value)
if (value === oldVal) return

let needRemove = false
if (value === '' || value == null) {
Expand Down Expand Up @@ -113,13 +99,14 @@ export function setDOMProp(el: any, key: string, value: any): void {
// do not warn if value is auto-coerced from nullish values
if (__DEV__ && !needRemove) {
warn(
`Failed setting prop "${key}" on <${tag.toLowerCase()}>: ` +
`Failed setting prop "${key}" on <${el.tagName.toLowerCase()}>: ` +
`value ${value} is invalid.`,
e,
)
}
}
needRemove && el.removeAttribute(key)
return value
}

export function setDynamicProp(el: Element, key: string, value: any): void {
Expand All @@ -138,6 +125,27 @@ export function setDynamicProp(el: Element, key: string, value: any): void {
? ((key = key.slice(1)), false)
: shouldSetAsProp(el, key, value, isSVG)
) {
if (key === 'innerHTML') {
setHtml(el, value)
return
}

if (key === 'textContent') {
setText(el, value)
return
}

const tag = el.tagName
if (
key === 'value' &&
tag !== 'PROGRESS' &&
// custom elements may use _value internally
!tag.includes('-')
) {
setValue(el, value)
return
}

setDOMProp(el, key, value)
} else {
// TODO special case for <input v-model type="checkbox">
Expand Down Expand Up @@ -220,7 +228,7 @@ export function setText(el: Node, ...values: any[]): void {
export function setHtml(el: Element, value: any): void {
const oldVal = recordPropMetadata(el, 'innerHTML', value)
if (value !== oldVal) {
el.innerHTML = value
el.innerHTML = value == null ? '' : value
}
}

Expand Down
1 change: 1 addition & 0 deletions packages/runtime-vapor/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ export {
setHtml,
setClass,
setAttr,
setValue,
setDOMProp,
setDynamicProp,
setDynamicProps,
Expand Down