patchProp.ts 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  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 { isOn, isString, isFunction, isModelListener } from '@vue/shared'
  7. import { RendererOptions } from '@vue/runtime-core'
  8. const nativeOnRE = /^on[a-z]/
  9. type DOMRendererOptions = RendererOptions<Node, Element>
  10. export const patchProp: DOMRendererOptions['patchProp'] = (
  11. el,
  12. key,
  13. prevValue,
  14. nextValue,
  15. isSVG = false,
  16. prevChildren,
  17. parentComponent,
  18. parentSuspense,
  19. unmountChildren
  20. ) => {
  21. if (key === 'class') {
  22. patchClass(el, nextValue, isSVG)
  23. } else if (key === 'style') {
  24. patchStyle(el, prevValue, nextValue)
  25. } else if (isOn(key)) {
  26. // ignore v-model listeners
  27. if (!isModelListener(key)) {
  28. patchEvent(el, key, prevValue, nextValue, parentComponent)
  29. }
  30. } else if (
  31. key[0] === '.'
  32. ? ((key = key.slice(1)), true)
  33. : key[0] === '^'
  34. ? ((key = key.slice(1)), false)
  35. : shouldSetAsProp(el, key, nextValue, isSVG)
  36. ) {
  37. patchDOMProp(
  38. el,
  39. key,
  40. nextValue,
  41. prevChildren,
  42. parentComponent,
  43. parentSuspense,
  44. unmountChildren
  45. )
  46. } else {
  47. // special case for <input v-model type="checkbox"> with
  48. // :true-value & :false-value
  49. // store value as dom properties since non-string values will be
  50. // stringified.
  51. if (key === 'true-value') {
  52. ;(el as any)._trueValue = nextValue
  53. } else if (key === 'false-value') {
  54. ;(el as any)._falseValue = nextValue
  55. }
  56. patchAttr(el, key, nextValue, isSVG, parentComponent)
  57. }
  58. }
  59. function shouldSetAsProp(
  60. el: Element,
  61. key: string,
  62. value: unknown,
  63. isSVG: boolean
  64. ) {
  65. if (isSVG) {
  66. // most keys must be set as attribute on svg elements to work
  67. // ...except innerHTML & textContent
  68. if (key === 'innerHTML' || key === 'textContent') {
  69. return true
  70. }
  71. // or native onclick with function values
  72. if (key in el && nativeOnRE.test(key) && isFunction(value)) {
  73. return true
  74. }
  75. return false
  76. }
  77. // spellcheck and draggable are numerated attrs, however their
  78. // corresponding DOM properties are actually booleans - this leads to
  79. // setting it with a string "false" value leading it to be coerced to
  80. // `true`, so we need to always treat them as attributes.
  81. // Note that `contentEditable` doesn't have this problem: its DOM
  82. // property is also enumerated string values.
  83. if (key === 'spellcheck' || key === 'draggable') {
  84. return false
  85. }
  86. // #1787, #2840 form property on form elements is readonly and must be set as
  87. // attribute.
  88. if (key === 'form') {
  89. return false
  90. }
  91. // #1526 <input list> must be set as attribute
  92. if (key === 'list' && el.tagName === 'INPUT') {
  93. return false
  94. }
  95. // #2766 <textarea type> must be set as attribute
  96. if (key === 'type' && el.tagName === 'TEXTAREA') {
  97. return false
  98. }
  99. // native onclick with string value, must be set as attribute
  100. if (nativeOnRE.test(key) && isString(value)) {
  101. return false
  102. }
  103. return key in el
  104. }