wip: class/style fallthrough compat

This commit is contained in:
Evan You 2021-04-21 22:04:26 -04:00
parent a75b00c558
commit 12abd4af85
5 changed files with 66 additions and 32 deletions

View File

@ -0,0 +1,22 @@
import { isOn } from '@vue/shared'
import { ComponentInternalInstance } from '../component'
import { DeprecationTypes, isCompatEnabled } from './compatConfig'
export function shouldSkipAttr(
key: string,
instance: ComponentInternalInstance
): boolean {
if (
(key === 'class' || key === 'style') &&
isCompatEnabled(DeprecationTypes.INSTANCE_ATTRS_CLASS_STYLE, instance)
) {
return true
}
if (
isOn(key) &&
isCompatEnabled(DeprecationTypes.INSTANCE_LISTENERS, instance)
) {
return true
}
return false
}

View File

@ -216,15 +216,15 @@ const deprecationData: Record<DeprecationTypes, DeprecationData> = {
}, },
[DeprecationTypes.INSTANCE_ATTRS_CLASS_STYLE]: { [DeprecationTypes.INSTANCE_ATTRS_CLASS_STYLE]: {
message: message: componentName =>
`vm.$attrs now includes class and style bindings passed from parent. ` + `Component <${componentName}> has \`inheritAttrs: false\` but is ` +
`Components with inheritAttrs: false will no longer auto-inherit ` + `relying on class/style fallthrough from parent. In Vue 3, class/style ` +
`class/style on its root element. If your code relies on this behavior, ` + `are now included in $attrs and will no longer fallthrough when ` +
`you may see broken styling and need to adjust your CSS. Otherwise, ` + `inheritAttrs is false. If you are already using v-bind="$attrs" on ` +
`you can disable the compat behavior and suppress this warning with:` + `component root it should render the same end result. ` +
`\n\n configureCompat({ ${ `If you are binding $attrs to a non-root element and expecting ` +
DeprecationTypes.INSTANCE_ATTRS_CLASS_STYLE `class/style to fallthrough on root, you will need to now manually bind ` +
}: false )\n`, `them on root via :class="$attrs.class".`,
link: `https://v3.vuejs.org/guide/migration/attrs-includes-class-style.html` link: `https://v3.vuejs.org/guide/migration/attrs-includes-class-style.html`
}, },

View File

@ -4,7 +4,6 @@ import { getCompatChildren } from './instanceChildren'
import { import {
DeprecationTypes, DeprecationTypes,
assertCompatEnabled, assertCompatEnabled,
checkCompatEnabled,
isCompatEnabled isCompatEnabled
} from './compatConfig' } from './compatConfig'
import { off, on, once } from './instanceEventEmitter' import { off, on, once } from './instanceEventEmitter'
@ -75,14 +74,6 @@ export function installCompatInstanceProperties(map: PublicPropertiesMap) {
return __DEV__ ? shallowReadonly(i.slots) : i.slots return __DEV__ ? shallowReadonly(i.slots) : i.slots
}, },
// overrides existing accessor
$attrs: i => {
if (__DEV__ && i.type.inheritAttrs === false) {
checkCompatEnabled(DeprecationTypes.INSTANCE_ATTRS_CLASS_STYLE, i)
}
return __DEV__ ? shallowReadonly(i.attrs) : i.attrs
},
$on: i => on.bind(null, i), $on: i => on.bind(null, i),
$once: i => once.bind(null, i), $once: i => once.bind(null, i),
$off: i => off.bind(null, i), $off: i => off.bind(null, i),

View File

@ -20,8 +20,7 @@ import {
isReservedProp, isReservedProp,
EMPTY_ARR, EMPTY_ARR,
def, def,
extend, extend
isOn
} from '@vue/shared' } from '@vue/shared'
import { warn } from './warning' import { warn } from './warning'
import { import {
@ -37,6 +36,7 @@ import { AppContext } from './apiCreateApp'
import { createPropsDefaultThis } from './compat/props' import { createPropsDefaultThis } from './compat/props'
import { isCompatEnabled, softAssertCompatEnabled } from './compat/compatConfig' import { isCompatEnabled, softAssertCompatEnabled } from './compat/compatConfig'
import { DeprecationTypes } from './compat/compatConfig' import { DeprecationTypes } from './compat/compatConfig'
import { shouldSkipAttr } from './compat/attrsFallthrough'
export type ComponentPropsOptions<P = Data> = export type ComponentPropsOptions<P = Data> =
| ComponentObjectPropsOptions<P> | ComponentObjectPropsOptions<P>
@ -229,11 +229,7 @@ export function updateProps(
) )
} }
} else { } else {
if ( if (__COMPAT__ && shouldSkipAttr(key, instance)) {
__COMPAT__ &&
isOn(key) &&
isCompatEnabled(DeprecationTypes.INSTANCE_LISTENERS, instance)
) {
continue continue
} }
if (value !== attrs[key]) { if (value !== attrs[key]) {
@ -341,11 +337,7 @@ function setFullProps(
// Any non-declared (either as a prop or an emitted event) props are put // Any non-declared (either as a prop or an emitted event) props are put
// into a separate `attrs` object for spreading. Make sure to preserve // into a separate `attrs` object for spreading. Make sure to preserve
// original key casing // original key casing
if ( if (__COMPAT__ && shouldSkipAttr(key, instance)) {
__COMPAT__ &&
isOn(key) &&
isCompatEnabled(DeprecationTypes.INSTANCE_LISTENERS, instance)
) {
continue continue
} }
if (value !== attrs[key]) { if (value !== attrs[key]) {

View File

@ -1,7 +1,8 @@
import { import {
ComponentInternalInstance, ComponentInternalInstance,
FunctionalComponent, FunctionalComponent,
Data Data,
getComponentName
} from './component' } from './component'
import { import {
VNode, VNode,
@ -20,6 +21,11 @@ import { isHmrUpdating } from './hmr'
import { NormalizedProps } from './componentProps' import { NormalizedProps } from './componentProps'
import { isEmitListener } from './componentEmits' import { isEmitListener } from './componentEmits'
import { setCurrentRenderingInstance } from './componentRenderContext' import { setCurrentRenderingInstance } from './componentRenderContext'
import {
DeprecationTypes,
isCompatEnabled,
warnDeprecation
} from './compat/compatConfig'
/** /**
* dev only flag to track whether $attrs was used during render. * dev only flag to track whether $attrs was used during render.
@ -117,7 +123,7 @@ export function renderComponentRoot(
;[root, setRoot] = getChildRoot(result) ;[root, setRoot] = getChildRoot(result)
} }
if (Component.inheritAttrs !== false && fallthroughAttrs) { if (fallthroughAttrs && Component.inheritAttrs !== false) {
const keys = Object.keys(fallthroughAttrs) const keys = Object.keys(fallthroughAttrs)
const { shapeFlag } = root const { shapeFlag } = root
if (keys.length) { if (keys.length) {
@ -175,6 +181,29 @@ export function renderComponentRoot(
} }
} }
if (
__COMPAT__ &&
isCompatEnabled(DeprecationTypes.INSTANCE_ATTRS_CLASS_STYLE, instance) &&
vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT &&
(root.shapeFlag & ShapeFlags.ELEMENT ||
root.shapeFlag & ShapeFlags.COMPONENT)
) {
const { class: cls, style } = vnode.props || {}
if (cls || style) {
if (__DEV__ && Component.inheritAttrs === false) {
warnDeprecation(
DeprecationTypes.INSTANCE_ATTRS_CLASS_STYLE,
instance,
getComponentName(instance.type)
)
}
root = cloneVNode(root, {
class: cls,
style: style
})
}
}
// inherit directives // inherit directives
if (vnode.dirs) { if (vnode.dirs) {
if (__DEV__ && !isElementRoot(root)) { if (__DEV__ && !isElementRoot(root)) {