Does not work with SSR and dynamic components
Closed this issue · 7 comments
When using SSR, the ID counter keeps incrementing with every page view, but dynamic components using $id
get a fresh ID generated on the client, with a small counter. This leads to a mismatch like this:
<component :is="'h2'" :aria-describedby="$id('description')">
Basic info
</component>
<p :id="$id('description')">
Just basic info
</p>
Generates:
<h2 aria-describedby="uid-12-description">Basic info</h2>
<p id="uid-108-description">Just basic info</p>
I don't know if it's possible but ideally every render should restart with the counter at 1
. Otherwise a long-running server process on a popular website will quickly run the counter into millions.
Right now my workaround is to call vm.$id
inside the mounted
hook and store the ID as component data, which fixes the mismatch issue, but doesn't stop the server from incrementing the counter to infinity.
Disclaimer: I haven't used SSR myself so this might be wrong! If I understand correctly, I see two issues here.
-
Based on what I see here (https://ssr.vuejs.org/guide/#integrating-with-a-server), the pattern is to call
Vue.use(UniqueId)
once when starting up the server. The internal counter which is reset on every page load with client-side rendering is never reset in SSR. This causes longer and longer ids that can eventually overflowNumber.MAX_SAFE_INTEGER
. I guess this could also be an issue also with client-side rendering for a very long-running application, but I'm not sure how its can be avoided in that case. -
The other issue is the mismatch between the ids rendered on the server and the ids rendered later on the client after client-side hydration. The server will start at id
N
while the client will start at id1
when the first client-side component is created. With your example using one (or few) ids, you get around that by delaying the assignment of the#id
-related attributes to when the components are mounted on the client-side.
I can see an easy fix for issue 1, but issue 2 is more involved. Perhaps something like the data-server-rendered
special attribute in https://ssr.vuejs.org/guide/hydration.html to pass-on the ids generated on the server and/or the "first server-side id used for this request" to the client. Is there any guarantee in Vue that SSR components will be created in the same order on the client as they were on the server?
Is it possible to do a minimal example somewhere (like codepen, etc.) so I can test the SSR version and experiment?
@berniegp Correct, those are the issues I see too.
I'm not sure about guarantees when it comes to rendering order on the server and on the client, but I doubt that we can rely on that.
I've set up an example on CodeSandbox, and strangely enough there seems to be some synchronisation after client-side hydration. The rendered text gets the correct counter, but not not the id
attribute.
Here it is on CodeSandbox (using Nuxt.js):
https://codesandbox.io/s/nifty-frost-yhso3
Thanks for the code example. I can reproduce it locally and try to figure out what can be done.
Are you aware of what communication channels exist between the server-side rendered version and the client? I know Vue uses a data attribute itself (data-server-rendered
) and there seems to be a way to transfer store state to the client as well but I don't really understand it. Is there any other facility available?
Also, how did you implement this:
Right now my workaround is to call vm.$id inside the mounted hook and store the ID as component data, which fixes the mismatch issue
I haven't found how to pass component data from the server to the client.
It looks like you found a bug with reactivity and client side hydration. I reported it here: vuejs/vue#11398
Also, how did you implement this:
Right now my workaround is to call vm.$id inside the mounted hook and store the ID as component data, which fixes the mismatch issue
I haven't found how to pass component data from the server to the client.
I'm just generating my IDs on the client side from within the mounted
hook. That means I only assign IDs during client-side rendering.
It looks like you found a bug with reactivity and client side hydration. I reported it here: vuejs/vue#11398
Hmm... I'll follow the thread! :)
Development of this plugin is discontinued. It still works perfectly well with Vue2, but I don't have time to port it to Vue3 and maintain it anymore.
This is a really small plugin so feel free to its code and add it directly to your project. Good luck!
There's also a fork that probably works with Vue3 here: https://github.com/snoozbuster/vue-unique-id