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