wip: component v-model compat
This commit is contained in:
		
							parent
							
								
									f05d6dfd98
								
							
						
					
					
						commit
						183f9b0013
					
				@ -1,5 +1,7 @@
 | 
			
		||||
import { hasOwn, isArray } from '@vue/shared/src'
 | 
			
		||||
import {
 | 
			
		||||
  ComponentInternalInstance,
 | 
			
		||||
  ComponentOptions,
 | 
			
		||||
  formatComponentName,
 | 
			
		||||
  getComponentName,
 | 
			
		||||
  getCurrentInstance,
 | 
			
		||||
@ -52,6 +54,7 @@ export const enum DeprecationTypes {
 | 
			
		||||
 | 
			
		||||
  COMPONENT_ASYNC = 'COMPONENT_ASYNC',
 | 
			
		||||
  COMPONENT_FUNCTIONAL = 'COMPONENT_FUNCTIONAL',
 | 
			
		||||
  COMPONENT_V_MODEL = 'COMPONENT_V_MODEL',
 | 
			
		||||
 | 
			
		||||
  RENDER_FUNCTION = 'RENDER_FUNCTION'
 | 
			
		||||
}
 | 
			
		||||
@ -345,6 +348,32 @@ const deprecationData: Record<DeprecationTypes, DeprecationData> = {
 | 
			
		||||
    link: `https://v3.vuejs.org/guide/migration/functional-components.html`
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  [DeprecationTypes.COMPONENT_V_MODEL]: {
 | 
			
		||||
    message: (comp: ComponentOptions) => {
 | 
			
		||||
      const configMsg =
 | 
			
		||||
        `opt-in to ` +
 | 
			
		||||
        `Vue 3 behavior on a per-component basis with \`compatConfig: { ${
 | 
			
		||||
          DeprecationTypes.COMPONENT_V_MODEL
 | 
			
		||||
        }: false }\`.`
 | 
			
		||||
      if (
 | 
			
		||||
        comp.props && isArray(comp.props)
 | 
			
		||||
          ? comp.props.includes('modelValue')
 | 
			
		||||
          : hasOwn(comp.props, 'modelValue')
 | 
			
		||||
      ) {
 | 
			
		||||
        return (
 | 
			
		||||
          `Component delcares "modelValue" prop, which is Vue 3 usage, but ` +
 | 
			
		||||
          `is running under Vue 2 compat v-model behavior. You can ${configMsg}`
 | 
			
		||||
        )
 | 
			
		||||
      }
 | 
			
		||||
      return (
 | 
			
		||||
        `v-model usage on component has changed in Vue 3. Component that expects ` +
 | 
			
		||||
        `to work with v-model should now use the "modelValue" prop and emit the ` +
 | 
			
		||||
        `"update:modelValue" event. You can update the usage and then ${configMsg}`
 | 
			
		||||
      )
 | 
			
		||||
    },
 | 
			
		||||
    link: `https://v3.vuejs.org/guide/migration/v-model.html`
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  [DeprecationTypes.RENDER_FUNCTION]: {
 | 
			
		||||
    message:
 | 
			
		||||
      `Vue 3's render function API has changed. ` +
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										71
									
								
								packages/runtime-core/src/compat/vModel.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								packages/runtime-core/src/compat/vModel.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,71 @@
 | 
			
		||||
import { ShapeFlags } from '@vue/shared'
 | 
			
		||||
import { ComponentInternalInstance, ComponentOptions } from '../component'
 | 
			
		||||
import { callWithErrorHandling, ErrorCodes } from '../errorHandling'
 | 
			
		||||
import { VNode } from '../vnode'
 | 
			
		||||
import { popWarningContext, pushWarningContext } from '../warning'
 | 
			
		||||
import { isCompatEnabled } from './compatConfig'
 | 
			
		||||
import { DeprecationTypes, warnDeprecation } from './deprecations'
 | 
			
		||||
 | 
			
		||||
const defaultModelMapping = {
 | 
			
		||||
  prop: 'value',
 | 
			
		||||
  event: 'input'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const compatModelEventPrefix = `onModelCompat:`
 | 
			
		||||
 | 
			
		||||
const warnedTypes = new WeakSet()
 | 
			
		||||
 | 
			
		||||
export function convertLegacyVModelProps(vnode: VNode) {
 | 
			
		||||
  const { type, shapeFlag, props, dynamicProps } = vnode
 | 
			
		||||
  if (shapeFlag & ShapeFlags.COMPONENT && props && 'modelValue' in props) {
 | 
			
		||||
    if (
 | 
			
		||||
      !isCompatEnabled(
 | 
			
		||||
        DeprecationTypes.COMPONENT_V_MODEL,
 | 
			
		||||
        // this is a special case where we want to use the vnode component's
 | 
			
		||||
        // compat config instead of the current rendering instance (which is the
 | 
			
		||||
        // parent of the component that exposes v-model)
 | 
			
		||||
        { type } as any
 | 
			
		||||
      )
 | 
			
		||||
    ) {
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (__DEV__ && !warnedTypes.has(type as ComponentOptions)) {
 | 
			
		||||
      pushWarningContext(vnode)
 | 
			
		||||
      warnDeprecation(DeprecationTypes.COMPONENT_V_MODEL, { type } as any, type)
 | 
			
		||||
      popWarningContext()
 | 
			
		||||
      warnedTypes.add(type as ComponentOptions)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const { prop, event } = (type as any).model || defaultModelMapping
 | 
			
		||||
    props[prop] = props.modelValue
 | 
			
		||||
    delete props.modelValue
 | 
			
		||||
    // important: update dynamic props
 | 
			
		||||
    if (dynamicProps) {
 | 
			
		||||
      dynamicProps[dynamicProps.indexOf('modelValue')] = prop
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    props[compatModelEventPrefix + event] = props['onUpdate:modelValue']
 | 
			
		||||
    delete props['onUpdate:modelValue']
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function compatModelEmit(
 | 
			
		||||
  instance: ComponentInternalInstance,
 | 
			
		||||
  event: string,
 | 
			
		||||
  args: any[]
 | 
			
		||||
) {
 | 
			
		||||
  if (!isCompatEnabled(DeprecationTypes.COMPONENT_V_MODEL, instance)) {
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
  const props = instance.vnode.props
 | 
			
		||||
  const modelHandler = props && props[compatModelEventPrefix + event]
 | 
			
		||||
  if (modelHandler) {
 | 
			
		||||
    callWithErrorHandling(
 | 
			
		||||
      modelHandler,
 | 
			
		||||
      instance,
 | 
			
		||||
      ErrorCodes.COMPONENT_EVENT_HANDLER,
 | 
			
		||||
      args
 | 
			
		||||
    )
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -687,9 +687,9 @@ export function finishComponentSetup(
 | 
			
		||||
  if (
 | 
			
		||||
    __COMPAT__ &&
 | 
			
		||||
    Component.render &&
 | 
			
		||||
    isCompatEnabled(DeprecationTypes.RENDER_FUNCTION)
 | 
			
		||||
    isCompatEnabled(DeprecationTypes.RENDER_FUNCTION, instance)
 | 
			
		||||
  ) {
 | 
			
		||||
    warnDeprecation(DeprecationTypes.RENDER_FUNCTION)
 | 
			
		||||
    warnDeprecation(DeprecationTypes.RENDER_FUNCTION, instance)
 | 
			
		||||
    const originalRender = Component.render
 | 
			
		||||
    Component.render = function compatRender() {
 | 
			
		||||
      return originalRender.call(this, compatH)
 | 
			
		||||
 | 
			
		||||
@ -21,7 +21,8 @@ import { warn } from './warning'
 | 
			
		||||
import { UnionToIntersection } from './helpers/typeUtils'
 | 
			
		||||
import { devtoolsComponentEmit } from './devtools'
 | 
			
		||||
import { AppContext } from './apiCreateApp'
 | 
			
		||||
import { emit as compatEmit } from './compat/instanceEventEmitter'
 | 
			
		||||
import { emit as compatInstanceEmit } from './compat/instanceEventEmitter'
 | 
			
		||||
import { compatModelEventPrefix, compatModelEmit } from './compat/vModel'
 | 
			
		||||
 | 
			
		||||
export type ObjectEmitsOptions = Record<
 | 
			
		||||
  string,
 | 
			
		||||
@ -57,7 +58,14 @@ export function emit(
 | 
			
		||||
      propsOptions: [propsOptions]
 | 
			
		||||
    } = instance
 | 
			
		||||
    if (emitsOptions) {
 | 
			
		||||
      if (!(event in emitsOptions) && !event.startsWith('hook:')) {
 | 
			
		||||
      if (
 | 
			
		||||
        !(event in emitsOptions) &&
 | 
			
		||||
        !(
 | 
			
		||||
          __COMPAT__ &&
 | 
			
		||||
          (event.startsWith('hook:') ||
 | 
			
		||||
            event.startsWith(compatModelEventPrefix))
 | 
			
		||||
        )
 | 
			
		||||
      ) {
 | 
			
		||||
        if (!propsOptions || !(toHandlerKey(event) in propsOptions)) {
 | 
			
		||||
          warn(
 | 
			
		||||
            `Component emitted event "${event}" but it is neither declared in ` +
 | 
			
		||||
@ -151,7 +159,8 @@ export function emit(
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (__COMPAT__) {
 | 
			
		||||
    return compatEmit(instance, event, args)
 | 
			
		||||
    compatModelEmit(instance, event, args)
 | 
			
		||||
    return compatInstanceEmit(instance, event, args)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -348,7 +348,7 @@ function resolvePropValue(
 | 
			
		||||
          value = propsDefaults[key] = defaultValue.call(
 | 
			
		||||
            __COMPAT__ &&
 | 
			
		||||
            __DEV__ &&
 | 
			
		||||
            isCompatEnabled(DeprecationTypes.PROPS_DEFAULT_THIS)
 | 
			
		||||
            isCompatEnabled(DeprecationTypes.PROPS_DEFAULT_THIS, instance)
 | 
			
		||||
              ? createPropsDefaultThis(key)
 | 
			
		||||
              : null,
 | 
			
		||||
            props
 | 
			
		||||
 | 
			
		||||
@ -42,6 +42,7 @@ import { NULL_DYNAMIC_COMPONENT } from './helpers/resolveAssets'
 | 
			
		||||
import { hmrDirtyComponents } from './hmr'
 | 
			
		||||
import { setCompiledSlotRendering } from './helpers/renderSlot'
 | 
			
		||||
import { convertLegacyComponent } from './compat/component'
 | 
			
		||||
import { convertLegacyVModelProps } from './compat/vModel'
 | 
			
		||||
 | 
			
		||||
export const Fragment = (Symbol(__DEV__ ? 'Fragment' : undefined) as any) as {
 | 
			
		||||
  __isFragment: true
 | 
			
		||||
@ -469,6 +470,10 @@ function _createVNode(
 | 
			
		||||
    currentBlock.push(vnode)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (__COMPAT__) {
 | 
			
		||||
    convertLegacyVModelProps(vnode)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return vnode
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user