diff --git a/packages/runtime-core/__tests__/rendererAttrsFallthrough.spec.ts b/packages/runtime-core/__tests__/rendererAttrsFallthrough.spec.ts
index 86425ca01..d358431d6 100644
--- a/packages/runtime-core/__tests__/rendererAttrsFallthrough.spec.ts
+++ b/packages/runtime-core/__tests__/rendererAttrsFallthrough.spec.ts
@@ -301,6 +301,34 @@ describe('attribute fallthrough', () => {
expect(root.innerHTML).toMatch(`
1
`)
})
+ // #3741
+ it('should not fallthrough with inheritAttrs: false from mixins', () => {
+ const Parent = {
+ render() {
+ return h(Child, { foo: 1, class: 'parent' })
+ }
+ }
+
+ const mixin = {
+ inheritAttrs: false
+ }
+
+ const Child = defineComponent({
+ mixins: [mixin],
+ props: ['foo'],
+ render() {
+ return h('div', this.foo)
+ }
+ })
+
+ const root = document.createElement('div')
+ document.body.appendChild(root)
+ render(h(Parent), root)
+
+ // should not contain class
+ expect(root.innerHTML).toMatch(`1
`)
+ })
+
it('explicit spreading with inheritAttrs: false', () => {
const Parent = {
render() {
diff --git a/packages/runtime-core/src/component.ts b/packages/runtime-core/src/component.ts
index aa92b3ed2..ec1aa65b3 100644
--- a/packages/runtime-core/src/component.ts
+++ b/packages/runtime-core/src/component.ts
@@ -285,6 +285,12 @@ export interface ComponentInternalInstance {
*/
emitsOptions: ObjectEmitsOptions | null
+ /**
+ * resolved inheritAttrs options
+ * @internal
+ */
+ inheritAttrs?: boolean
+
// the rest are only for stateful components ---------------------------------
// main proxy that serves as the public instance (`this`)
@@ -469,6 +475,9 @@ export function createComponentInstance(
// props default value
propsDefaults: EMPTY_OBJ,
+ // inheritAttrs
+ inheritAttrs: type.inheritAttrs,
+
// state
ctx: EMPTY_OBJ,
data: EMPTY_OBJ,
diff --git a/packages/runtime-core/src/componentOptions.ts b/packages/runtime-core/src/componentOptions.ts
index d281b3c51..e467ecbc8 100644
--- a/packages/runtime-core/src/componentOptions.ts
+++ b/packages/runtime-core/src/componentOptions.ts
@@ -567,17 +567,14 @@ export function applyOptions(
errorCaptured,
serverPrefetch,
// public API
- expose
+ expose,
+ inheritAttrs
} = options
const publicThis = instance.proxy!
const ctx = instance.ctx
const globalMixins = instance.appContext.mixins
- if (asMixin && render && instance.render === NOOP) {
- instance.render = render as InternalRenderFunction
- }
-
// applyOptions is called non-as-mixin once per instance
if (!asMixin) {
shouldCacheAccess = false
@@ -755,17 +752,6 @@ export function applyOptions(
})
}
- // asset options.
- // To reduce memory usage, only components with mixins or extends will have
- // resolved asset registry attached to instance.
- if (asMixin) {
- resolveInstanceAssets(instance, options, COMPONENTS)
- resolveInstanceAssets(instance, options, DIRECTIVES)
- if (__COMPAT__ && isCompatEnabled(DeprecationTypes.FILTERS, instance)) {
- resolveInstanceAssets(instance, options, FILTERS)
- }
- }
-
// lifecycle options
if (!asMixin) {
callSyncHook(
@@ -831,6 +817,27 @@ export function applyOptions(
warn(`The \`expose\` option is ignored when used in mixins.`)
}
}
+
+ // options that are handled when creating the instance but also need to be
+ // applied from mixins
+ if (asMixin) {
+ if (render && instance.render === NOOP) {
+ instance.render = render as InternalRenderFunction
+ }
+
+ if (inheritAttrs != null && instance.type.inheritAttrs == null) {
+ instance.inheritAttrs = inheritAttrs
+ }
+
+ // asset options.
+ // To reduce memory usage, only components with mixins or extends will have
+ // resolved asset registry attached to instance.
+ resolveInstanceAssets(instance, options, COMPONENTS)
+ resolveInstanceAssets(instance, options, DIRECTIVES)
+ if (__COMPAT__ && isCompatEnabled(DeprecationTypes.FILTERS, instance)) {
+ resolveInstanceAssets(instance, options, FILTERS)
+ }
+ }
}
function resolveInstanceAssets(
diff --git a/packages/runtime-core/src/componentRenderUtils.ts b/packages/runtime-core/src/componentRenderUtils.ts
index ca215e54c..0563ba592 100644
--- a/packages/runtime-core/src/componentRenderUtils.ts
+++ b/packages/runtime-core/src/componentRenderUtils.ts
@@ -55,7 +55,8 @@ export function renderComponentRoot(
renderCache,
data,
setupState,
- ctx
+ ctx,
+ inheritAttrs
} = instance
let result
@@ -123,7 +124,7 @@ export function renderComponentRoot(
;[root, setRoot] = getChildRoot(result)
}
- if (fallthroughAttrs && Component.inheritAttrs !== false) {
+ if (fallthroughAttrs && inheritAttrs !== false) {
const keys = Object.keys(fallthroughAttrs)
const { shapeFlag } = root
if (keys.length) {
@@ -190,7 +191,7 @@ export function renderComponentRoot(
) {
const { class: cls, style } = vnode.props || {}
if (cls || style) {
- if (__DEV__ && Component.inheritAttrs === false) {
+ if (__DEV__ && inheritAttrs === false) {
warnDeprecation(
DeprecationTypes.INSTANCE_ATTRS_CLASS_STYLE,
instance,
diff --git a/packages/server-renderer/src/render.ts b/packages/server-renderer/src/render.ts
index eaba85605..2b78dda03 100644
--- a/packages/server-renderer/src/render.ts
+++ b/packages/server-renderer/src/render.ts
@@ -132,8 +132,7 @@ function renderComponentSubTree(
if (ssrRender) {
// optimized
// resolve fallthrough attrs
- let attrs =
- instance.type.inheritAttrs !== false ? instance.attrs : undefined
+ let attrs = instance.inheritAttrs !== false ? instance.attrs : undefined
let hasCloned = false
let cur = instance