diff --git a/packages/compiler-core/src/compat/compatConfig.ts b/packages/compiler-core/src/compat/compatConfig.ts index babfa526..d6870b47 100644 --- a/packages/compiler-core/src/compat/compatConfig.ts +++ b/packages/compiler-core/src/compat/compatConfig.ts @@ -54,10 +54,10 @@ const deprecationData: Record = { [CompilerDeprecationTypes.COMPILER_V_BIND_OBJECT_ORDER]: { message: `v-bind="obj" usage is now order sensitive and behaves like JavaScript ` + - `object spread: it will now overwrite an existing attribute that appears ` + - `before v-bind in the case of conflicting keys. To retain 2.x behavior, ` + - `move v-bind to and make it the first attribute. If all occurences ` + - `of this warning are working as intended, you can suppress it.`, + `object spread: it will now overwrite an existing non-mergeable attribute ` + + `that appears before v-bind in the case of conflict. ` + + `To retain 2.x behavior, move v-bind to make it the first attribute. ` + + `You can also suppress this warning if the usage is intended.`, link: `https://v3.vuejs.org/guide/migration/v-bind.html` }, @@ -98,17 +98,24 @@ function getCompatValue( } } +export function isCompatEnabled( + key: CompilerDeprecationTypes, + context: ParserContext | TransformContext +) { + const mode = getCompatValue('MODE', context) + const value = getCompatValue(key, context) + // in v3 mode, only enable if explicitly set to true + // otherwise enable for any non-false value + return mode === 3 ? value === true : value !== false +} + export function checkCompatEnabled( key: CompilerDeprecationTypes, context: ParserContext | TransformContext, loc: SourceLocation | null, ...args: any[] ): boolean { - const mode = getCompatValue('MODE', context) - const value = getCompatValue(key, context) - // in v3 mode, only enable if explicitly set to true - // otherwise enable for any non-false value - const enabled = mode === 3 ? value === true : value !== false + const enabled = isCompatEnabled(key, context) if (__DEV__ && enabled) { warnDeprecation(key, context, loc, ...args) } diff --git a/packages/compiler-core/src/transforms/transformElement.ts b/packages/compiler-core/src/transforms/transformElement.ts index cbb456a8..214acbc5 100644 --- a/packages/compiler-core/src/transforms/transformElement.ts +++ b/packages/compiler-core/src/transforms/transformElement.ts @@ -55,6 +55,11 @@ import { import { buildSlots } from './vSlot' import { getConstantType } from './hoistStatic' import { BindingTypes } from '../options' +import { + checkCompatEnabled, + CompilerDeprecationTypes, + isCompatEnabled +} from '../compat/compatConfig' // some directive transforms (e.g. v-model) may return a symbol for runtime // import, which should be used instead of a resolveDirective call. @@ -450,8 +455,8 @@ export function buildProps( } else { // directives const { name, arg, exp, loc } = prop - const isBind = name === 'bind' - const isOn = name === 'on' + const isVBind = name === 'bind' + const isVOn = name === 'on' // skip v-slot - it is handled by its dedicated transform. if (name === 'slot') { @@ -469,17 +474,17 @@ export function buildProps( // skip v-is and :is on if ( name === 'is' || - (isBind && isComponentTag(tag) && isBindKey(arg, 'is')) + (isVBind && isComponentTag(tag) && isBindKey(arg, 'is')) ) { continue } // skip v-on in SSR compilation - if (isOn && ssr) { + if (isVOn && ssr) { continue } // special case for v-bind and v-on with no argument - if (!arg && (isBind || isOn)) { + if (!arg && (isVBind || isVOn)) { hasDynamicKeys = true if (exp) { if (properties.length) { @@ -488,7 +493,49 @@ export function buildProps( ) properties = [] } - if (isBind) { + if (isVBind) { + if (__COMPAT__) { + if (__DEV__) { + const hasOverridableKeys = mergeArgs.some(arg => { + if (arg.type === NodeTypes.JS_OBJECT_EXPRESSION) { + return arg.properties.some(({ key }) => { + if ( + key.type !== NodeTypes.SIMPLE_EXPRESSION || + !key.isStatic + ) { + return true + } + return ( + key.content !== 'class' && + key.content !== 'style' && + !isOn(key.content) + ) + }) + } else { + // dynamic expression + return true + } + }) + if (hasOverridableKeys) { + checkCompatEnabled( + CompilerDeprecationTypes.COMPILER_V_BIND_OBJECT_ORDER, + context, + loc + ) + } + } + + if ( + isCompatEnabled( + CompilerDeprecationTypes.COMPILER_V_BIND_OBJECT_ORDER, + context + ) + ) { + mergeArgs.unshift(exp) + continue + } + } + mergeArgs.push(exp) } else { // v-on="obj" -> toHandlers(obj) @@ -502,7 +549,7 @@ export function buildProps( } else { context.onError( createCompilerError( - isBind + isVBind ? ErrorCodes.X_V_BIND_NO_EXPRESSION : ErrorCodes.X_V_ON_NO_EXPRESSION, loc