patchProp.ts 3.1 KB

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