ScopeIds not working on router-view in SSR after upgrading from vue@3.0.7
Version
3.0.9
Reproduction link
https://github.com/Olli1080/vue-3-ssr-reproduction
Steps to reproduce
Run npm i npm start
Open browser on localhost:3000
What is expected?
The scopeId should be applied to the router-view and the header causing the content background to be limegreen
What is actually happening?
The background is white and the scopeId is only missing on the router-view item in the dom.
The background changes to limegreen after navigation with the scopeId applied
Ran into this after upgrading from vue@3.0.7 to vue@3.0.8 and vue@3.0.9
A hint, this problem occurs when rendering components with manually written render function between ssr optimized components. E.g.
ComA
is scoped, and SSR-optimized:
const _withId =_withScopeId("scope-id")
const CompA = {
ssrRender = _withId((_ctx, _push, _parent, _attrs) => {
const RouterView = _resolveComponent("RouterView")
// Render the <RouterView> component
_push(_ssrRenderComponent(RouterView, _attrs, null, _parent))
})
}
RouterView
has a hand-written render function:
const RouterView = {
render() { return h(ViewComp) }
}
ViewComp
is also optimized by SSR:
const ViewComp = {
ssrRender(_ctx, _push, _parent, _attrs) {
_push(`<p${_attrs}></p>`)
}
}
This issue occurs in the renderComponentSubTree function, alternating rendering of SSR-optimized and non-SSR-optimized components will break the inheritance of scopeId
.
Edit: The following test case can be used as a minimum reproduction:
test('repro', async () => {
const Child = {
ssrRender: (ctx: any, push: any, parent: any, attrs: any) => {
push(`<div${ssrRenderAttrs(attrs)}></div>`)
}
}
const Middle = {
render() {
return h(Child)
}
}
const Comp = {
__scopeId: 'parent',
ssrRender: (ctx: any, push: any, parent: any) => {
push(ssrRenderComponent(Middle, null, null, parent))
}
}
const result = await renderToString(createApp(Comp)) // output: `<div></div>`
expect(result).toBe(`<div parent></div>`)
})
The resolveScopeId function is also problematic. When a component is SSR-optimized, it will no longer have subTree
, this will cause the inheritance of scopeId to be broken, the following example can be used to reproduce:
const Child = {
render() {
return h('div')
}
}
const Comp2 = {
__scopeId: 'parent2',
ssrRender: (ctx: any, push: any, parent: any) => {
push(ssrRenderComponent(Child, null, null, parent))
}
}
const Comp = {
__scopeId: 'parent',
ssrRender: (ctx: any, push: any, parent: any) => {
push(ssrRenderComponent(Comp2, null, null, parent))
}
}
async function run () {
// Should output: `<div parent parent2></div>`
// Actual behavior: `<div parent2></div>`
console.log(await renderToString(createApp(Comp)))
}
run()
I have some ideas, and that requires changes to the compiler, but it has a relatively large impact.....
@HcySunYang I suspect we found a similar issue on VTU-next
This template <transition><div></div></transition>
(note the lack of content in the div) throws:
Uncaught TypeError: children is null
filterSingleRoot https://sfc.vuejs.org/vue.runtime.esm-browser.js:2171
setScopeId https://sfc.vuejs.org/vue.runtime.esm-browser.js:5079
mountElement https://sfc.vuejs.org/vue.runtime.esm-browser.js:5032
Is this the same root cause, or should I open a different issue?
@HcySunYang your case in https://github.com/vuejs/vue-next/issues/3513#issuecomment-810167160 won't happen because SSR-optimized HOC components pass attrs
down:
const Comp = {
__scopeId: 'parent',
- ssrRender: (ctx: any, push: any, parent: any) => {
- push(ssrRenderComponent(Comp2, null, null, parent))
+ ssrRender: (ctx: any, push: any, parent: any, attrs: any) => {
+ push(ssrRenderComponent(Comp2, attrs, null, parent))
}
}
playground link (see SSR output)