componentVModel.ts 2.5 KB

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