patchProp.ts 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  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 isNativeOn = (key: string) =>
  9. key.charCodeAt(0) === 111 /* o */ &&
  10. key.charCodeAt(1) === 110 /* n */ &&
  11. // lowercase letter
  12. key.charCodeAt(2) > 96 &&
  13. key.charCodeAt(2) < 123
  14. type DOMRendererOptions = RendererOptions<Node, Element>
  15. export const patchProp: DOMRendererOptions['patchProp'] = (
  16. el,
  17. key,
  18. prevValue,
  19. nextValue,
  20. namespace,
  21. prevChildren,
  22. parentComponent,
  23. parentSuspense,
  24. unmountChildren
  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(
  44. el,
  45. key,
  46. nextValue,
  47. prevChildren,
  48. parentComponent,
  49. parentSuspense,
  50. unmountChildren
  51. )
  52. } else {
  53. // special case for <input v-model type="checkbox"> with
  54. // :true-value & :false-value
  55. // store value as dom properties since non-string values will be
  56. // stringified.
  57. if (key === 'true-value') {
  58. ;(el as any)._trueValue = nextValue
  59. } else if (key === 'false-value') {
  60. ;(el as any)._falseValue = nextValue
  61. }
  62. patchAttr(el, key, nextValue, isSVG, parentComponent)
  63. }
  64. }
  65. function shouldSetAsProp(
  66. el: Element,
  67. key: string,
  68. value: unknown,
  69. isSVG: boolean
  70. ) {
  71. if (isSVG) {
  72. // most keys must be set as attribute on svg elements to work
  73. // ...except innerHTML & textContent
  74. if (key === 'innerHTML' || key === 'textContent') {
  75. return true
  76. }
  77. // or native onclick with function values
  78. if (key in el && isNativeOn(key) && isFunction(value)) {
  79. return true
  80. }
  81. return false
  82. }
  83. // these are enumerated attrs, however their corresponding DOM properties
  84. // are actually booleans - this leads to setting it with a string "false"
  85. // value leading it to be coerced to `true`, so we need to always treat
  86. // them as attributes.
  87. // Note that `contentEditable` doesn't have this problem: its DOM
  88. // property is also enumerated string values.
  89. if (key === 'spellcheck' || key === 'draggable' || key === 'translate') {
  90. return false
  91. }
  92. // #1787, #2840 form property on form elements is readonly and must be set as
  93. // attribute.
  94. if (key === 'form') {
  95. return false
  96. }
  97. // #1526 <input list> must be set as attribute
  98. if (key === 'list' && el.tagName === 'INPUT') {
  99. return false
  100. }
  101. // #2766 <textarea type> must be set as attribute
  102. if (key === 'type' && el.tagName === 'TEXTAREA') {
  103. return false
  104. }
  105. // #8780 the width or height of embedded tags must be set as attribute
  106. if (key === 'width' || key === 'height') {
  107. const tag = el.tagName
  108. if (
  109. tag === 'IMG' ||
  110. tag === 'VIDEO' ||
  111. tag === 'CANVAS' ||
  112. tag === 'SOURCE'
  113. ) {
  114. return false
  115. }
  116. }
  117. // native onclick with string value, must be set as attribute
  118. if (isNativeOn(key) && isString(value)) {
  119. return false
  120. }
  121. return key in el
  122. }