vue: serverPrefetch doesn't work as expected
Version
2.6.10
Steps to reproduce
Suppose we have a component:
export default {
data(){
return { foo: null}
}
async serverPrefetch(){
this.foo = 'serverPrefetch'
},
created(){
this.foo = 'created'
},
}
foo is initially null, then created() updates it to created and this reflects in the html interpolitatiion, but when serverPrefetch() updates this.foo it is not reflected on the rendered html, despite being called afterwards.
What is expected?
serverPrefetch() should reflect rendered and updated data variables when rendered on SSR
What is actually happening?
serverPrefetch() doesn’t update the rendered view with new data after being called
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Comments: 36 (17 by maintainers)
To conclude: your steps 2 and 4 in your diagram are not technically possible.
@DominusVilicus the code you originally posted is working as expected (per test case). By design, a component’s
serverPrefetchshould only affectdataof its own (or child components via passed props). The only case that it “doesn’t work” is that when a component’sserverPrefetchis mutating state that doesn’t belong to itself.Ok, let’s say you have
serverPrefetchon both A, B & C, and that A, B & C all rely on different data from your API to correctly render. For example, A needs an API result to decide whether to render B or C with av-if. B & C needs some other APIs calls to display data in their template too, and theirserverPrefetchrelies on some props passed by A.What you are proposing will happen:
serverPrefetchis collectedserverPrefetchs, which means the one from AThere is a big issue here: neither B nor C where created, but you expected one of them to be created.
How to fix this? The only way is to render A again after the first
serverPrefetchs awaiting, so that the VNode for B or C is created. So let’s say we add a new rule: after theserverPrefetchis awaited, we re-render the component.Here is what would happen:
serverPrefetchis collectedserverPrefetchs, which means the one from AserverPrefechis collectedserverPrefetchs, which means the one from BWe can already see we did multiple renders of the same component, which will affect performance. But it can get worse from there: what if B
serverPrefetchactually mutated data that A depends on?serverPrefetchis collectedserverPrefetchs, which means the one from ASo now we actually rendered A four times and B three times! And this can go on and on and on…
Takeaways
1 - It’s impossible to have one and only one step with all the possible
serverPrefetchin the app and call/await them all at once. The code can’t guess if B or C would have been rendered and if we need to call theirserverPrefetch. Also, if, like in the above example, BserverPrefetchrelies on data passed via props by A, it can’t work without having a intermediate step where A re-renders to pass the value via props to B. 2 - We can’t render all the components in the app at once either after waiting for all the possibleserverPrefetch(which is impossible, see takeaway 1). Also, component instances are not created until the parent component is rendered, so theirserverPrefetchs don’t even exist yet. 3 - Mutating state that a parent component depends on is a very bad idea when doing SSR since it can lead to multiple cascading updates. You don’t want the HTML page to take ages to be generated and sent to the client. 4 - We can’t call/awaitserverPrefetchin a component instance after rendering it, otherwise we have to render it multiple times (and even call itsserverPrefetchagain).is this expected behaviour @yyx990803 @Akryum, wouldn’t it be more practical and expected for all the
serverPrefetchs to be called before the render functions? especially when in the ssr vue examples state that the vuex store can be updated with serverPrefetchhttps://ssr.vuejs.org/guide/data.html#data-store https://ssr.vuejs.org/api/#serverprefetch
while the hydration isn’t failing, the server is rendering a different state (because of the reasons described in the above comment). Is this what you’d expect serverPrefetch to function like?
In my use case,
index.vueis usinglayout.vueas a component (wrapping index.vue inapp.vue’s tag,s sinceapp.vueis the “parent” component, it generates all the necessary global state mixins (as to prevent code duplication).app.vuehas it’s own component,header.vue(which is rendered afterapp.vueand receives the correct state.What’s happening in order is:
import indeximport appimport headerindex.serverPrefetch()index.render()app.serverPrefetch()app.render()header.serverPretch()header.render()(this recieves correct global state fromapp.vueas it is rendered afterwards)In my opinion, what should be happening is:
import indeximport appimport headerindex.serverPrefetch()app.serverPrefetch()header.serverPretch()index.render()//recieves same global state as all componentsapp.render()header.render()@DominusVilicus You should update the global state in a component which is a parent of all the components requiring it.
While i’m not well versed in SSR(it all seems like magic to me) I will interject this:
I believe any data collection SHOULD occur prior to a component being rendered to me, both server side and client side…the model should be exactly the same as soon as you provide inconsistencies, you provide surface area for bugs
Consider a simple example where the component conditionally renders one component or another depending on its state:
Now you can see we can’t create the component instances of
MyComponentAnorMyComponentBbecause we don’t know which one will be created before actually rendering the current component.It seems the render function for each component is being called before other components
serverPrefetchis.Shouldn’t all
serverPrefetchs for all components in thecomponents: {...}be called before any component is actually rendered?Reactivity is entirely disabled during SSR, so there will be no “updates” - the state can be mutated, but it must happen before the render function is called.
I figured out the issue.
Vue.observeabledoesn’t trigger a re-render on server components when they import a child component which created theVue.observeableIt’s pretty complex, but can someone clarify whether components are re-rendered after
serverPrefetchupdates aVue.observablethat multiple components rely on. It seems not to be happening in my case