Skip to content

Commit c0427b4

Browse files
committed
fix(sfc/scoped-style): inherit scopeId through nested HOCs with inheritAttrs: false
fix #1988
1 parent 5b82c48 commit c0427b4

File tree

6 files changed

+120
-42
lines changed

6 files changed

+120
-42
lines changed

packages/runtime-core/__tests__/helpers/scopeId.spec.ts

+28-3
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ describe('scopeId runtime support', () => {
3434
const root = nodeOps.createElement('div')
3535
render(h(App), root)
3636
expect(serializeInner(root)).toBe(
37-
`<div parent><div parent child></div></div>`
37+
`<div parent><div child parent></div></div>`
3838
)
3939
})
4040

@@ -67,14 +67,39 @@ describe('scopeId runtime support', () => {
6767
// - scopeId from parent
6868
// - slotted scopeId (with `-s` postfix) from child (the tree owner)
6969
expect(serializeInner(root)).toBe(
70-
`<div parent child>` +
70+
`<div child parent>` +
7171
`<div parent child-s></div>` +
7272
// component inside slot should have:
7373
// - scopeId from template context
7474
// - slotted scopeId from slot owner
7575
// - its own scopeId
76-
`<span parent child-s child2></span>` +
76+
`<span child2 parent child-s></span>` +
7777
`</div>`
7878
)
7979
})
80+
81+
// #1988
82+
test('should inherit scopeId through nested HOCs with inheritAttrs: false', () => {
83+
const withParentId = withScopeId('parent')
84+
const App = {
85+
__scopeId: 'parent',
86+
render: withParentId(() => {
87+
return h(Child)
88+
})
89+
}
90+
91+
function Child() {
92+
return h(Child2, { class: 'foo' })
93+
}
94+
95+
function Child2() {
96+
return h('div')
97+
}
98+
Child2.inheritAttrs = false
99+
100+
const root = nodeOps.createElement('div')
101+
render(h(App), root)
102+
103+
expect(serializeInner(root)).toBe(`<div parent></div>`)
104+
})
80105
})

packages/runtime-core/src/componentRenderUtils.ts

-17
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ export function renderComponentRoot(
4242
): VNode {
4343
const {
4444
type: Component,
45-
parent,
4645
vnode,
4746
proxy,
4847
withProxy,
@@ -172,22 +171,6 @@ export function renderComponentRoot(
172171
}
173172
}
174173

175-
// inherit scopeId
176-
const scopeId = vnode.scopeId
177-
// vite#536: if subtree root is created from parent slot if would already
178-
// have the correct scopeId, in this case adding the scopeId will cause
179-
// it to be removed if the original slot vnode is reused.
180-
const needScopeId = scopeId && root.scopeId !== scopeId
181-
const treeOwnerId = parent && parent.type.__scopeId
182-
const slotScopeId =
183-
treeOwnerId && treeOwnerId !== scopeId ? treeOwnerId + '-s' : null
184-
if (needScopeId || slotScopeId) {
185-
const extras: Data = {}
186-
if (needScopeId) extras[scopeId!] = ''
187-
if (slotScopeId) extras[slotScopeId] = ''
188-
root = cloneVNode(root, extras)
189-
}
190-
191174
// inherit directives
192175
if (vnode.dirs) {
193176
if (__DEV__ && !isElementRoot(root)) {

packages/runtime-core/src/renderer.ts

+52-9
Original file line numberDiff line numberDiff line change
@@ -745,15 +745,31 @@ function baseCreateRenderer(
745745
}
746746
}
747747
// scopeId
748-
if (scopeId) {
749-
hostSetScopeId(el, scopeId)
750-
}
751-
const treeOwnerId = parentComponent && parentComponent.type.__scopeId
752-
// vnode's own scopeId and the current patched component's scopeId is
753-
// different - this is a slot content node.
754-
if (treeOwnerId && treeOwnerId !== scopeId) {
755-
hostSetScopeId(el, treeOwnerId + '-s')
756-
}
748+
setScopeId(el, scopeId, vnode, parentComponent)
749+
// if (scopeId) {
750+
// hostSetScopeId(el, scopeId)
751+
// }
752+
// if (parentComponent) {
753+
// const treeOwnerId = parentComponent.type.__scopeId
754+
// // vnode's own scopeId and the current patched component's scopeId is
755+
// // different - this is a slot content node.
756+
// if (treeOwnerId && treeOwnerId !== scopeId) {
757+
// hostSetScopeId(el, treeOwnerId + '-s')
758+
// }
759+
// const parentScopeId =
760+
// vnode === parentComponent.subTree && parentComponent.vnode.scopeId
761+
// if (parentScopeId) {
762+
// hostSetScopeId(el, parentScopeId)
763+
// if (parentComponent.parent) {
764+
// const treeOwnerId = parentComponent.parent.type.__scopeId
765+
// // vnode's own scopeId and the current patched component's scopeId is
766+
// // different - this is a slot content node.
767+
// if (treeOwnerId && treeOwnerId !== parentScopeId) {
768+
// hostSetScopeId(el, treeOwnerId + '-s')
769+
// }
770+
// }
771+
// }
772+
// }
757773
}
758774
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
759775
Object.defineProperty(el, '__vnode', {
@@ -791,6 +807,33 @@ function baseCreateRenderer(
791807
}
792808
}
793809

810+
const setScopeId = (
811+
el: RendererElement,
812+
scopeId: string | false | null,
813+
vnode: VNode,
814+
parentComponent: ComponentInternalInstance | null
815+
) => {
816+
if (scopeId) {
817+
hostSetScopeId(el, scopeId)
818+
}
819+
if (parentComponent) {
820+
const treeOwnerId = parentComponent.type.__scopeId
821+
// vnode's own scopeId and the current patched component's scopeId is
822+
// different - this is a slot content node.
823+
if (treeOwnerId && treeOwnerId !== scopeId) {
824+
hostSetScopeId(el, treeOwnerId + '-s')
825+
}
826+
if (vnode === parentComponent.subTree) {
827+
setScopeId(
828+
el,
829+
parentComponent.vnode.scopeId,
830+
parentComponent.vnode,
831+
parentComponent.parent
832+
)
833+
}
834+
}
835+
}
836+
794837
const mountChildren: MountChildrenFn = (
795838
children,
796839
container,

packages/server-renderer/__tests__/renderToStream.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -595,7 +595,7 @@ describe('ssr: renderToStream', () => {
595595
}
596596

597597
expect(await renderToStream(h(Parent))).toBe(
598-
`<div data-v-test data-v-child><span data-v-test data-v-child-s>slot</span></div>`
598+
`<div data-v-child data-v-test><span data-v-test data-v-child-s>slot</span></div>`
599599
)
600600
})
601601
})

packages/server-renderer/__tests__/renderToString.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -551,7 +551,7 @@ describe('ssr: renderToString', () => {
551551
}
552552

553553
expect(await renderToString(h(Parent))).toBe(
554-
`<div data-v-test data-v-child><span data-v-test data-v-child-s>slot</span></div>`
554+
`<div data-v-child data-v-test><span data-v-test data-v-child-s>slot</span></div>`
555555
)
556556
})
557557
})

packages/server-renderer/src/render.ts

+38-11
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,11 @@ function renderComponentSubTree(
101101
const comp = instance.type as Component
102102
const { getBuffer, push } = createBuffer()
103103
if (isFunction(comp)) {
104-
renderVNode(push, renderComponentRoot(instance), instance)
104+
renderVNode(
105+
push,
106+
(instance.subTree = renderComponentRoot(instance)),
107+
instance
108+
)
105109
} else {
106110
if (!instance.render && !comp.ssrRender && isString(comp.template)) {
107111
comp.ssrRender = ssrCompile(comp.template, instance)
@@ -139,7 +143,11 @@ function renderComponentSubTree(
139143
)
140144
setCurrentRenderingInstance(null)
141145
} else if (instance.render) {
142-
renderVNode(push, renderComponentRoot(instance), instance)
146+
renderVNode(
147+
push,
148+
(instance.subTree = renderComponentRoot(instance)),
149+
instance
150+
)
143151
} else {
144152
warn(
145153
`Component ${
@@ -225,15 +233,7 @@ function renderElementVNode(
225233
openTag += ssrRenderAttrs(props, tag)
226234
}
227235

228-
if (scopeId) {
229-
openTag += ` ${scopeId}`
230-
const treeOwnerId = parentComponent && parentComponent.type.__scopeId
231-
// vnode's own scopeId and the current rendering component's scopeId is
232-
// different - this is a slot content node.
233-
if (treeOwnerId && treeOwnerId !== scopeId) {
234-
openTag += ` ${treeOwnerId}-s`
235-
}
236-
}
236+
openTag += resolveScopeId(scopeId, vnode, parentComponent)
237237

238238
push(openTag + `>`)
239239
if (!isVoidTag(tag)) {
@@ -265,6 +265,33 @@ function renderElementVNode(
265265
}
266266
}
267267

268+
function resolveScopeId(
269+
scopeId: string | null,
270+
vnode: VNode,
271+
parentComponent: ComponentInternalInstance | null
272+
) {
273+
let res = ``
274+
if (scopeId) {
275+
res = ` ${scopeId}`
276+
}
277+
if (parentComponent) {
278+
const treeOwnerId = parentComponent.type.__scopeId
279+
// vnode's own scopeId and the current rendering component's scopeId is
280+
// different - this is a slot content node.
281+
if (treeOwnerId && treeOwnerId !== scopeId) {
282+
res += ` ${treeOwnerId}-s`
283+
}
284+
if (vnode === parentComponent.subTree) {
285+
res += resolveScopeId(
286+
parentComponent.vnode.scopeId,
287+
parentComponent.vnode,
288+
parentComponent.parent
289+
)
290+
}
291+
}
292+
return res
293+
}
294+
268295
function applySSRDirectives(
269296
vnode: VNode,
270297
rawProps: VNodeProps | null,

0 commit comments

Comments
 (0)