From 23d2eaac4ea93bd7b0eee350e9645a3a39170c5f Mon Sep 17 00:00:00 2001 From: Cong Date: Tue, 15 Aug 2023 16:18:09 +0800 Subject: [PATCH 1/5] fix(runtime-core): should not fallthrough attrs if attr declared in root component as prop --- packages/runtime-core/src/componentRenderUtils.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/runtime-core/src/componentRenderUtils.ts b/packages/runtime-core/src/componentRenderUtils.ts index d9de968a074..00afd454b83 100644 --- a/packages/runtime-core/src/componentRenderUtils.ts +++ b/packages/runtime-core/src/componentRenderUtils.ts @@ -2,7 +2,8 @@ import { ComponentInternalInstance, FunctionalComponent, Data, - getComponentName + getComponentName, + ConcreteComponent } from './component' import { VNode, @@ -133,8 +134,18 @@ export function renderComponentRoot( } if (fallthroughAttrs && inheritAttrs !== false) { + const { shapeFlag, type, props } = root + + if (shapeFlag & ShapeFlags.COMPONENT && props) { + Object.keys(fallthroughAttrs).forEach(key => { + if (key in props) { + const propsDef = (type as ConcreteComponent).props + if (propsDef && key in propsDef) delete fallthroughAttrs![key] + } + }) + } + const keys = Object.keys(fallthroughAttrs) - const { shapeFlag } = root if (keys.length) { if (shapeFlag & (ShapeFlags.ELEMENT | ShapeFlags.COMPONENT)) { if (propsOptions && keys.some(isModelListener)) { From 9008c7779a7246d00eaba21035fd0295a7d6a718 Mon Sep 17 00:00:00 2001 From: Cong Date: Tue, 15 Aug 2023 16:48:55 +0800 Subject: [PATCH 2/5] chore: add some comments --- packages/runtime-core/src/componentRenderUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/runtime-core/src/componentRenderUtils.ts b/packages/runtime-core/src/componentRenderUtils.ts index 00afd454b83..d9c6ed8603e 100644 --- a/packages/runtime-core/src/componentRenderUtils.ts +++ b/packages/runtime-core/src/componentRenderUtils.ts @@ -135,7 +135,7 @@ export function renderComponentRoot( if (fallthroughAttrs && inheritAttrs !== false) { const { shapeFlag, type, props } = root - + // fix #8969 should not fallthrough attr if it has been declared as a prop in the root component if (shapeFlag & ShapeFlags.COMPONENT && props) { Object.keys(fallthroughAttrs).forEach(key => { if (key in props) { From cf8237ce664dc3f6fc479c3a40ef0e1077c90d97 Mon Sep 17 00:00:00 2001 From: Cong Date: Tue, 15 Aug 2023 16:18:09 +0800 Subject: [PATCH 3/5] fix(runtime-core): should not fallthrough attrs if attr declared in root component as prop --- packages/runtime-core/src/componentRenderUtils.ts | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/runtime-core/src/componentRenderUtils.ts b/packages/runtime-core/src/componentRenderUtils.ts index d9de968a074..00afd454b83 100644 --- a/packages/runtime-core/src/componentRenderUtils.ts +++ b/packages/runtime-core/src/componentRenderUtils.ts @@ -2,7 +2,8 @@ import { ComponentInternalInstance, FunctionalComponent, Data, - getComponentName + getComponentName, + ConcreteComponent } from './component' import { VNode, @@ -133,8 +134,18 @@ export function renderComponentRoot( } if (fallthroughAttrs && inheritAttrs !== false) { + const { shapeFlag, type, props } = root + + if (shapeFlag & ShapeFlags.COMPONENT && props) { + Object.keys(fallthroughAttrs).forEach(key => { + if (key in props) { + const propsDef = (type as ConcreteComponent).props + if (propsDef && key in propsDef) delete fallthroughAttrs![key] + } + }) + } + const keys = Object.keys(fallthroughAttrs) - const { shapeFlag } = root if (keys.length) { if (shapeFlag & (ShapeFlags.ELEMENT | ShapeFlags.COMPONENT)) { if (propsOptions && keys.some(isModelListener)) { From 5b48778d4a1b1d5b48322bd72aed8d813dd4cfb5 Mon Sep 17 00:00:00 2001 From: Cong Date: Tue, 15 Aug 2023 16:48:55 +0800 Subject: [PATCH 4/5] chore: add some comments --- packages/runtime-core/src/componentRenderUtils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/runtime-core/src/componentRenderUtils.ts b/packages/runtime-core/src/componentRenderUtils.ts index 00afd454b83..d9c6ed8603e 100644 --- a/packages/runtime-core/src/componentRenderUtils.ts +++ b/packages/runtime-core/src/componentRenderUtils.ts @@ -135,7 +135,7 @@ export function renderComponentRoot( if (fallthroughAttrs && inheritAttrs !== false) { const { shapeFlag, type, props } = root - + // fix #8969 should not fallthrough attr if it has been declared as a prop in the root component if (shapeFlag & ShapeFlags.COMPONENT && props) { Object.keys(fallthroughAttrs).forEach(key => { if (key in props) { From 32cc97d198ad9784972e2c75f3a62409dfd021a5 Mon Sep 17 00:00:00 2001 From: Cong Date: Mon, 28 Aug 2023 09:40:01 +0800 Subject: [PATCH 5/5] test: add test case --- .../rendererAttrsFallthrough.spec.ts | 35 +++++++++++++++++++ .../runtime-core/src/componentRenderUtils.ts | 16 +++++++-- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/packages/runtime-core/__tests__/rendererAttrsFallthrough.spec.ts b/packages/runtime-core/__tests__/rendererAttrsFallthrough.spec.ts index 1de05b67b44..eceb7d54609 100644 --- a/packages/runtime-core/__tests__/rendererAttrsFallthrough.spec.ts +++ b/packages/runtime-core/__tests__/rendererAttrsFallthrough.spec.ts @@ -729,4 +729,39 @@ describe('attribute fallthrough', () => { expect(textBar).toBe('from GrandChild') expect(textFoo).toBe('from Child') }) + + // #9039 + it('should not fallthrough attr if it has been declared as a prop in the root component', () => { + const App = defineComponent({ + setup() { + return () => + h(Child, { + foo: '123' + }) + } + }) + + const Child = defineComponent({ + setup(_props) { + const foo = ref('456') + return () => + h(GrandChild, { + foo: foo.value + }) + } + }) + const GrandChild = defineComponent({ + props: ['foo'], + setup(_props) { + return () => h('span', null, _props.foo) + } + }) + + const root = document.createElement('div') + document.body.appendChild(root) + render(h(App), root) + + const node = root.children[0] as HTMLElement + expect(node.innerHTML).toBe('456') + }) }) diff --git a/packages/runtime-core/src/componentRenderUtils.ts b/packages/runtime-core/src/componentRenderUtils.ts index d9c6ed8603e..90ba1835ca0 100644 --- a/packages/runtime-core/src/componentRenderUtils.ts +++ b/packages/runtime-core/src/componentRenderUtils.ts @@ -16,7 +16,14 @@ import { blockStack } from './vnode' import { handleError, ErrorCodes } from './errorHandling' -import { PatchFlags, ShapeFlags, isOn, isModelListener } from '@vue/shared' +import { + PatchFlags, + ShapeFlags, + isOn, + isModelListener, + isObject, + isArray +} from '@vue/shared' import { warn } from './warning' import { isHmrUpdating } from './hmr' import { NormalizedProps } from './componentProps' @@ -140,7 +147,12 @@ export function renderComponentRoot( Object.keys(fallthroughAttrs).forEach(key => { if (key in props) { const propsDef = (type as ConcreteComponent).props - if (propsDef && key in propsDef) delete fallthroughAttrs![key] + if ( + propsDef && + ((isObject(propsDef) && key in propsDef) || + (isArray(propsDef) && propsDef.includes(key))) + ) + delete fallthroughAttrs![key] } }) }