patchProp.ts 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. import { patchClass } from './modules/class'
  2. import { patchStyle } from './modules/style'
  3. import { patchAttr } from './modules/attrs'
  4. import { patchDOMProp } from './modules/props'
  5. import { patchEvent } from './modules/events'
  6. import {
  7. camelize,
  8. isFunction,
  9. isModelListener,
  10. isNativeOn,
  11. isOn,
  12. isString,
  13. shouldSetAsAttr,
  14. } from '@vue/shared'
  15. import type { RendererOptions } from '@vue/runtime-core'
  16. import type { VueElement } from './apiCustomElement'
  17. type DOMRendererOptions = RendererOptions<Node, Element>
  18. export const patchProp: DOMRendererOptions['patchProp'] = (
  19. el,
  20. key,
  21. prevValue,
  22. nextValue,
  23. namespace,
  24. parentComponent,
  25. ) => {
  26. const isSVG = namespace === 'svg'
  27. if (key === 'class') {
  28. patchClass(el, nextValue, isSVG)
  29. } else if (key === 'style') {
  30. patchStyle(el, prevValue, nextValue)
  31. } else if (isOn(key)) {
  32. // ignore v-model listeners
  33. if (!isModelListener(key)) {
  34. patchEvent(el, key, prevValue, nextValue, parentComponent)
  35. }
  36. } else if (
  37. key[0] === '.'
  38. ? ((key = key.slice(1)), true)
  39. : key[0] === '^'
  40. ? ((key = key.slice(1)), false)
  41. : shouldSetAsProp(el, key, nextValue, isSVG)
  42. ) {
  43. patchDOMProp(el, key, nextValue, parentComponent)
  44. // #6007 also set form state as attributes so they work with
  45. // <input type="reset"> or libs / extensions that expect attributes
  46. // #11163 custom elements may use value as an prop and set it as object
  47. if (
  48. !el.tagName.includes('-') &&
  49. (key === 'value' || key === 'checked' || key === 'selected')
  50. ) {
  51. patchAttr(el, key, nextValue, isSVG, parentComponent, key !== 'value')
  52. }
  53. } else if (
  54. // #11081 force set props for possible async custom element
  55. (el as VueElement)._isVueCE &&
  56. (/[A-Z]/.test(key) || !isString(nextValue))
  57. ) {
  58. patchDOMProp(el, camelize(key), nextValue, parentComponent, key)
  59. } else {
  60. // special case for <input v-model type="checkbox"> with
  61. // :true-value & :false-value
  62. // store value as dom properties since non-string values will be
  63. // stringified.
  64. if (key === 'true-value') {
  65. ;(el as any)._trueValue = nextValue
  66. } else if (key === 'false-value') {
  67. ;(el as any)._falseValue = nextValue
  68. }
  69. patchAttr(el, key, nextValue, isSVG, parentComponent)
  70. }
  71. }
  72. export function shouldSetAsProp(
  73. el: Element,
  74. key: string,
  75. value: unknown,
  76. isSVG: boolean,
  77. ): boolean {
  78. if (isSVG) {
  79. // most keys must be set as attribute on svg elements to work
  80. // ...except innerHTML & textContent
  81. if (key === 'innerHTML' || key === 'textContent') {
  82. return true
  83. }
  84. // or native onclick with function values
  85. if (key in el && isNativeOn(key) && isFunction(value)) {
  86. return true
  87. }
  88. return false
  89. }
  90. if (shouldSetAsAttr(el.tagName, key)) {
  91. return false
  92. }
  93. // native onclick with string value, must be set as attribute
  94. if (isNativeOn(key) && isString(value)) {
  95. return false
  96. }
  97. return key in el
  98. }