Subscribe on changes!

ScopeIds not working on router-view in SSR after upgrading from vue@3.0.7

avatar
Mar 29th 2021

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

avatar
Mar 30th 2021

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>`)
  })
avatar
Mar 30th 2021

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()
avatar
Mar 30th 2021

I have some ideas, and that requires changes to the compiler, but it has a relatively large impact.....

avatar
Mar 30th 2021

@HcySunYang Maybe I could manually add the compiler hints to the render functions to vue router.

avatar
Mar 30th 2021

@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

See https://sfc.vuejs.org/#eyJBcHAudnVlIjoiPHRlbXBsYXRlPlxuXHQ8dHJhbnNpdGlvbj48ZGl2PjwvZGl2PjwvdHJhbnNpdGlvbj5cbjwvdGVtcGxhdGU+In0=

Is this the same root cause, or should I open a different issue?

avatar
Mar 30th 2021

@cexbrayat that's a runtime error, definitely a different issue.

avatar
Mar 30th 2021

@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)

avatar
Mar 30th 2021

@cexbrayat the bug you mentioned is fixed in 9cf75258