Skip to content

Commit 12abd4a

Browse files
committed
wip: class/style fallthrough compat
1 parent a75b00c commit 12abd4a

File tree

5 files changed

+66
-32
lines changed

5 files changed

+66
-32
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { isOn } from '@vue/shared'
2+
import { ComponentInternalInstance } from '../component'
3+
import { DeprecationTypes, isCompatEnabled } from './compatConfig'
4+
5+
export function shouldSkipAttr(
6+
key: string,
7+
instance: ComponentInternalInstance
8+
): boolean {
9+
if (
10+
(key === 'class' || key === 'style') &&
11+
isCompatEnabled(DeprecationTypes.INSTANCE_ATTRS_CLASS_STYLE, instance)
12+
) {
13+
return true
14+
}
15+
if (
16+
isOn(key) &&
17+
isCompatEnabled(DeprecationTypes.INSTANCE_LISTENERS, instance)
18+
) {
19+
return true
20+
}
21+
return false
22+
}

packages/runtime-core/src/compat/compatConfig.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -216,15 +216,15 @@ const deprecationData: Record<DeprecationTypes, DeprecationData> = {
216216
},
217217

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

packages/runtime-core/src/compat/instance.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { getCompatChildren } from './instanceChildren'
44
import {
55
DeprecationTypes,
66
assertCompatEnabled,
7-
checkCompatEnabled,
87
isCompatEnabled
98
} from './compatConfig'
109
import { off, on, once } from './instanceEventEmitter'
@@ -75,14 +74,6 @@ export function installCompatInstanceProperties(map: PublicPropertiesMap) {
7574
return __DEV__ ? shallowReadonly(i.slots) : i.slots
7675
},
7776

78-
// overrides existing accessor
79-
$attrs: i => {
80-
if (__DEV__ && i.type.inheritAttrs === false) {
81-
checkCompatEnabled(DeprecationTypes.INSTANCE_ATTRS_CLASS_STYLE, i)
82-
}
83-
return __DEV__ ? shallowReadonly(i.attrs) : i.attrs
84-
},
85-
8677
$on: i => on.bind(null, i),
8778
$once: i => once.bind(null, i),
8879
$off: i => off.bind(null, i),

packages/runtime-core/src/componentProps.ts

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@ import {
2020
isReservedProp,
2121
EMPTY_ARR,
2222
def,
23-
extend,
24-
isOn
23+
extend
2524
} from '@vue/shared'
2625
import { warn } from './warning'
2726
import {
@@ -37,6 +36,7 @@ import { AppContext } from './apiCreateApp'
3736
import { createPropsDefaultThis } from './compat/props'
3837
import { isCompatEnabled, softAssertCompatEnabled } from './compat/compatConfig'
3938
import { DeprecationTypes } from './compat/compatConfig'
39+
import { shouldSkipAttr } from './compat/attrsFallthrough'
4040

4141
export type ComponentPropsOptions<P = Data> =
4242
| ComponentObjectPropsOptions<P>
@@ -229,11 +229,7 @@ export function updateProps(
229229
)
230230
}
231231
} else {
232-
if (
233-
__COMPAT__ &&
234-
isOn(key) &&
235-
isCompatEnabled(DeprecationTypes.INSTANCE_LISTENERS, instance)
236-
) {
232+
if (__COMPAT__ && shouldSkipAttr(key, instance)) {
237233
continue
238234
}
239235
if (value !== attrs[key]) {
@@ -341,11 +337,7 @@ function setFullProps(
341337
// Any non-declared (either as a prop or an emitted event) props are put
342338
// into a separate `attrs` object for spreading. Make sure to preserve
343339
// original key casing
344-
if (
345-
__COMPAT__ &&
346-
isOn(key) &&
347-
isCompatEnabled(DeprecationTypes.INSTANCE_LISTENERS, instance)
348-
) {
340+
if (__COMPAT__ && shouldSkipAttr(key, instance)) {
349341
continue
350342
}
351343
if (value !== attrs[key]) {

packages/runtime-core/src/componentRenderUtils.ts

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import {
22
ComponentInternalInstance,
33
FunctionalComponent,
4-
Data
4+
Data,
5+
getComponentName
56
} from './component'
67
import {
78
VNode,
@@ -20,6 +21,11 @@ import { isHmrUpdating } from './hmr'
2021
import { NormalizedProps } from './componentProps'
2122
import { isEmitListener } from './componentEmits'
2223
import { setCurrentRenderingInstance } from './componentRenderContext'
24+
import {
25+
DeprecationTypes,
26+
isCompatEnabled,
27+
warnDeprecation
28+
} from './compat/compatConfig'
2329

2430
/**
2531
* dev only flag to track whether $attrs was used during render.
@@ -117,7 +123,7 @@ export function renderComponentRoot(
117123
;[root, setRoot] = getChildRoot(result)
118124
}
119125

120-
if (Component.inheritAttrs !== false && fallthroughAttrs) {
126+
if (fallthroughAttrs && Component.inheritAttrs !== false) {
121127
const keys = Object.keys(fallthroughAttrs)
122128
const { shapeFlag } = root
123129
if (keys.length) {
@@ -175,6 +181,29 @@ export function renderComponentRoot(
175181
}
176182
}
177183

184+
if (
185+
__COMPAT__ &&
186+
isCompatEnabled(DeprecationTypes.INSTANCE_ATTRS_CLASS_STYLE, instance) &&
187+
vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT &&
188+
(root.shapeFlag & ShapeFlags.ELEMENT ||
189+
root.shapeFlag & ShapeFlags.COMPONENT)
190+
) {
191+
const { class: cls, style } = vnode.props || {}
192+
if (cls || style) {
193+
if (__DEV__ && Component.inheritAttrs === false) {
194+
warnDeprecation(
195+
DeprecationTypes.INSTANCE_ATTRS_CLASS_STYLE,
196+
instance,
197+
getComponentName(instance.type)
198+
)
199+
}
200+
root = cloneVNode(root, {
201+
class: cls,
202+
style: style
203+
})
204+
}
205+
}
206+
178207
// inherit directives
179208
if (vnode.dirs) {
180209
if (__DEV__ && !isElementRoot(root)) {

0 commit comments

Comments
 (0)