componentVModel.ts 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. import { ShapeFlags, extend } from '@vue/shared'
  2. import type { ComponentInternalInstance, ComponentOptions } from '../component'
  3. import { createAppContext } from '../apiCreateApp'
  4. import { ErrorCodes, callWithErrorHandling } from '../errorHandling'
  5. import type { VNode } from '../vnode'
  6. import { popWarningContext, pushWarningContext } from '../warning'
  7. import {
  8. DeprecationTypes,
  9. isCompatEnabled,
  10. warnDeprecation,
  11. } from './compatConfig'
  12. export const compatModelEventPrefix = `onModelCompat:`
  13. const warnedTypes = new WeakSet()
  14. export function convertLegacyVModelProps(vnode: VNode): void {
  15. const { type, shapeFlag, props, dynamicProps } = vnode
  16. const comp = type as ComponentOptions
  17. if (shapeFlag & ShapeFlags.COMPONENT && props && 'modelValue' in props) {
  18. if (
  19. !isCompatEnabled(
  20. DeprecationTypes.COMPONENT_V_MODEL,
  21. // this is a special case where we want to use the vnode component's
  22. // compat config instead of the current rendering instance (which is the
  23. // parent of the component that exposes v-model)
  24. { type } as any,
  25. )
  26. ) {
  27. return
  28. }
  29. if (__DEV__ && !warnedTypes.has(comp)) {
  30. pushWarningContext(vnode)
  31. warnDeprecation(
  32. DeprecationTypes.COMPONENT_V_MODEL,
  33. {
  34. type,
  35. appContext: (vnode.ctx && vnode.ctx.appContext) || createAppContext(),
  36. } as any,
  37. comp,
  38. )
  39. popWarningContext()
  40. warnedTypes.add(comp)
  41. }
  42. // v3 compiled model code -> v2 compat props
  43. // modelValue -> value
  44. // onUpdate:modelValue -> onModelCompat:input
  45. const model = comp.model || {}
  46. applyModelFromMixins(model, comp.mixins)
  47. const { prop = 'value', event = 'input' } = model
  48. if (prop !== 'modelValue') {
  49. props[prop] = props.modelValue
  50. delete props.modelValue
  51. }
  52. // important: update dynamic props
  53. if (dynamicProps) {
  54. dynamicProps[dynamicProps.indexOf('modelValue')] = prop
  55. }
  56. props[compatModelEventPrefix + event] = props['onUpdate:modelValue']
  57. delete props['onUpdate:modelValue']
  58. }
  59. }
  60. function applyModelFromMixins(model: any, mixins?: ComponentOptions[]) {
  61. if (mixins) {
  62. mixins.forEach(m => {
  63. if (m.model) extend(model, m.model)
  64. if (m.mixins) applyModelFromMixins(model, m.mixins)
  65. })
  66. }
  67. }
  68. export function compatModelEmit(
  69. instance: ComponentInternalInstance,
  70. event: string,
  71. args: any[],
  72. ): void {
  73. if (!isCompatEnabled(DeprecationTypes.COMPONENT_V_MODEL, instance)) {
  74. return
  75. }
  76. const props = instance.vnode.props
  77. const modelHandler = props && props[compatModelEventPrefix + event]
  78. if (modelHandler) {
  79. callWithErrorHandling(
  80. modelHandler,
  81. instance,
  82. ErrorCodes.COMPONENT_EVENT_HANDLER,
  83. args,
  84. )
  85. }
  86. }