wip: attr coersion compat

This commit is contained in:
Evan You 2021-04-07 17:36:56 -04:00
parent cf6bcdf895
commit c1e5cfe7d6
2 changed files with 92 additions and 14 deletions

View File

@ -29,10 +29,14 @@ export const enum DeprecationTypes {
OPTIONS_BEFORE_DESTROY = 'OPTIONS_BEFORE_DESTROY', OPTIONS_BEFORE_DESTROY = 'OPTIONS_BEFORE_DESTROY',
OPTIONS_DESTROYED = 'OPTIONS_DESTROYED', OPTIONS_DESTROYED = 'OPTIONS_DESTROYED',
V_ON_KEYCODE_MODIFIER = 'V_ON_KEYCODE_MODIFIER',
PROPS_DEFAULT_THIS = 'PROPS_DEFAULT_THIS',
CUSTOM_DIR = 'CUSTOM_DIR',
WATCH_ARRAY = 'WATCH_ARRAY', WATCH_ARRAY = 'WATCH_ARRAY',
PROPS_DEFAULT_THIS = 'PROPS_DEFAULT_THIS',
V_ON_KEYCODE_MODIFIER = 'V_ON_KEYCODE_MODIFIER',
CUSTOM_DIR = 'CUSTOM_DIR',
ATTR_FALSE_VALUE = 'ATTR_FALSE_VALUE',
ATTR_ENUMERATED_COERSION = 'ATTR_ENUMERATED_COERSION',
TRANSITION_CLASSES = 'TRANSITION_CLASSES', TRANSITION_CLASSES = 'TRANSITION_CLASSES',
TRANSITION_GROUP_ROOT = 'TRANSITION_GROUP_ROOT' TRANSITION_GROUP_ROOT = 'TRANSITION_GROUP_ROOT'
@ -191,11 +195,16 @@ const deprecationData: Record<DeprecationTypes, DeprecationData> = {
message: `\`destroyed\` has been renamed to \`unmounted\`.` message: `\`destroyed\` has been renamed to \`unmounted\`.`
}, },
[DeprecationTypes.V_ON_KEYCODE_MODIFIER]: { [DeprecationTypes.WATCH_ARRAY]: {
message: message:
`Using keyCode as v-on modifier is no longer supported. ` + `"watch" option or vm.$watch on an array value will no longer ` +
`Use kebab-case key name modifiers instead.`, `trigger on array mutation unless the "deep" option is specified. ` +
link: `https://v3.vuejs.org/guide/migration/keycode-modifiers.html` `If current usage is intended, you can disable the compat behavior and ` +
`suppress this warning with:` +
`\n\n configureCompat({ ${
DeprecationTypes.WATCH_ARRAY
}: { enabled: false }})\n`,
link: `https://v3.vuejs.org/guide/migration/watch.html`
}, },
[DeprecationTypes.PROPS_DEFAULT_THIS]: { [DeprecationTypes.PROPS_DEFAULT_THIS]: {
@ -212,16 +221,38 @@ const deprecationData: Record<DeprecationTypes, DeprecationData> = {
link: `https://v3.vuejs.org/guide/migration/custom-directives.html` link: `https://v3.vuejs.org/guide/migration/custom-directives.html`
}, },
[DeprecationTypes.WATCH_ARRAY]: { [DeprecationTypes.V_ON_KEYCODE_MODIFIER]: {
message: message:
`"watch" option or vm.$watch on an array value will no longer ` + `Using keyCode as v-on modifier is no longer supported. ` +
`trigger on array mutation unless the "deep" option is specified. ` + `Use kebab-case key name modifiers instead.`,
`If current usage is intended, you can disable the compat behavior and ` + link: `https://v3.vuejs.org/guide/migration/keycode-modifiers.html`
`suppress this warning with:` + },
[DeprecationTypes.ATTR_FALSE_VALUE]: {
message: (name: string) =>
`Attribute "${name}" with v-bind value \`false\` will render ` +
`${name}="false" instead of removing it in Vue 3. To remove the attribute, ` +
`use \`null\` or \`undefined\` instead. If the usage is intended, ` +
`you can disable the compat behavior and suppress this warning with:` +
`\n\n configureCompat({ ${ `\n\n configureCompat({ ${
DeprecationTypes.WATCH_ARRAY DeprecationTypes.ATTR_FALSE_VALUE
}: { enabled: false }})\n`, }: { enabled: false }})\n`,
link: `https://v3.vuejs.org/guide/migration/watch.html` link: `https://v3.vuejs.org/guide/migration/attribute-coercion.html`
},
[DeprecationTypes.ATTR_ENUMERATED_COERSION]: {
message: (name: string, value: any, coerced: string) =>
`Enumerated attribute "${name}" with v-bind value \`${value}\` will ` +
`${
value === null ? `be removed` : `render the value as-is`
} instead of coercing the value to "${coerced}" in Vue 3. ` +
`Always use explicit "true" or "false" values for enumerated attributes. ` +
`If the usage is intended, ` +
`you can disable the compat behavior and suppress this warning with:` +
`\n\n configureCompat({ ${
DeprecationTypes.ATTR_ENUMERATED_COERSION
}: { enabled: false }})\n`,
link: `https://v3.vuejs.org/guide/migration/attribute-coercion.html`
}, },
[DeprecationTypes.TRANSITION_CLASSES]: { [DeprecationTypes.TRANSITION_CLASSES]: {

View File

@ -15,6 +15,10 @@ export function patchAttr(
el.setAttributeNS(xlinkNS, key, value) el.setAttributeNS(xlinkNS, key, value)
} }
} else { } else {
if (__COMPAT__ && compatCoerceAttr(el, key, value)) {
return
}
// note we are only checking boolean attributes that don't have a // note we are only checking boolean attributes that don't have a
// corresponding dom prop of the same name here. // corresponding dom prop of the same name here.
const isBoolean = isSpecialBooleanAttr(key) const isBoolean = isSpecialBooleanAttr(key)
@ -25,3 +29,46 @@ export function patchAttr(
} }
} }
} }
// 2.x compat
import { makeMap, NOOP } from '@vue/shared'
import { compatUtils, DeprecationTypes } from '@vue/runtime-core'
const isEnumeratedAttr = __COMPAT__
? /*#__PURE__*/ makeMap('contenteditable,draggable,spellcheck')
: NOOP
export function compatCoerceAttr(
el: Element,
key: string,
value: unknown
): boolean {
if (isEnumeratedAttr(key)) {
const v2CocercedValue =
value === null
? 'false'
: typeof value !== 'boolean' && value !== undefined
? 'true'
: null
if (
v2CocercedValue &&
compatUtils.softAssertCompatEnabled(
DeprecationTypes.ATTR_ENUMERATED_COERSION,
key,
value,
v2CocercedValue
)
) {
el.setAttribute(key, v2CocercedValue)
return true
}
} else if (
value === false &&
!isSpecialBooleanAttr(key) &&
compatUtils.softAssertCompatEnabled(DeprecationTypes.ATTR_FALSE_VALUE, key)
) {
el.removeAttribute(key)
return true
}
return false
}