Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Page redirection fails due to SSR mismatch #1382

Closed
jjyyxx opened this issue Mar 2, 2019 · 4 comments
Closed

Page redirection fails due to SSR mismatch #1382

jjyyxx opened this issue Mar 2, 2019 · 4 comments
Labels
has workaround Has a workaround topic: config Relates to VuePress config type: question or discussion Question or discussion

Comments

@jjyyxx
Copy link
Contributor

jjyyxx commented Mar 2, 2019

Feature request

What problem does this feature solve?

Sometimes, we need to redirect to another location before the page finishes rendering. For example, in the following snippet,

export function handleRedirectForCleanUrls (router) {
router.beforeEach((to, from, next) => {
if (isRouteExists(router, to.path)) {
next()
} else {
if (!/(\/|\.html)$/.test(to.path)) {
const endingSlashUrl = to.path + '/'
const endingHtmlUrl = to.path + '.html'
if (isRouteExists(router, endingHtmlUrl)) {
next(endingHtmlUrl)
} else if (isRouteExists(router, endingSlashUrl)) {
next(endingSlashUrl)
} else {
next()
}
} else if (/\/$/.test(to.path)) {
const endingHtmlUrl = to.path.replace(/\/$/, '') + '.html'
if (isRouteExists(router, endingHtmlUrl)) {
next(endingHtmlUrl)
} else {
next()
}
} else {
next()
}
}
})
}

VuePress will try to redirect unknown request /foo to /foo.html and /foo/. Other scenarios include redirecting / to /zh/ or /en/ based on navigator language.

The snippet referenced above will work fine for most of the conditions. But if the page needs redirecting before rendering starts, uses server-side rendering and the pages before and after redirecting do not share an identical structure, this will lead to the problem called SSR Mismatch.

An example of handleRedirectForCleanUrls to explain the mismatch

You request/foo in the browser, but the server can't find a direct match (without certain config), so a NotFound page /404.html will be returned. The beforeEach hook registered in handleRedirectForCleanUrls will redirect the router to /foo.html. Note that the DOM remains unchanged but the VDOM is replaced with /foo.html's. Normally, /404.html is a plain page without navbar and sidebar, but the new VDOM is a document page with such components. The DOM fails to match the VDOM, causing rendering error and the view will not be updated. This is an SSR Mismatch and its consequence.

Summarize the bug-like problem as follow.

  1. The server-side rendered pages need a hydration stage to be fully functional and the hydration stage needs corresponding DOM and VDOM structure (a correctly server-side rendered website could easily achieve this).

  2. But, when at least one of DOM and VDOM changes before hydration, DOM and VDOM mismatch will happen, especially in production distribution of Vue where the mismatch warning is indirect and the re-rendering fallback is removed (see this issue in the Vue repository).

  3. Redirecting may be the most reasonable way for such condition to happen as the VDOM is changed and the pages before and after redirecting do not have identical VDOM structure.

The feature request aims to solve this problem.

What does the proposed API look like?

What I propose here is that VuePress should handle the problem internally for end users and plugin developers, for example, by postponing the redirection.

If it can't be solved gracefully, at least, the document should explicitly point this caveat out.

How should this be implemented in your opinion?

I mentioned bug-like above because I know it is not a bug of Vue Server Renderer or Vue Router. Instead, it should be addressed by the app itself. But developers may easily ignore this and waste much time debugging because the bug only exists in build mode with server-side rendering enabled. Even VuePress' official code (the snippet referenced above) has such problem as well.

Are you willing to work on this yourself?

I'm willing to participate in. Actually, I have experimented for a while but can't solve the problem without glitch. So, I post it out and seek help.

@shigma shigma added type: question or discussion Question or discussion topic: config Relates to VuePress config labels Mar 2, 2019
@shigma
Copy link
Collaborator

shigma commented Mar 11, 2019

Also, Vue component, such as <TOC/>, can also lead to SSR mismatch if the corresponding page is opened without being redirected. Also, any <router-link> in an SSR-mismatched page will fail, which I think is indeed a serious problem.
I made a mistake. This problem exists in every markdown-it plugin (markdown-it-table-of-contents, markdown-it-toc, markdown-it-toc-and anchor...... you name it). Even in the 0.x documentation we can see an extra <p>. It's just using a component that expose the SSR mismatching problem.

@meteorlxy
Copy link
Member

Normal SSR

urlA -> server -> redirect urlB -> render pageB -> client get pageB -> vue hydrate pageB with pageB (OK)

Current VuePress "SSR"

urlA -> server -> "render" pageA -> client get pageA -> redirect urlB -> vue hydrate pageB with pageA (fuck!)

@shigma
Copy link
Collaborator

shigma commented Mar 12, 2019

I have created a plugin vuepress-plugin-dehydration to temporarily workaround this issue.

@shigma shigma closed this as completed Mar 12, 2019
@ulivz
Copy link
Member

ulivz commented Mar 12, 2019

I have created a repro for users to test this issue, Thanks to @shigma's great work!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
has workaround Has a workaround topic: config Relates to VuePress config type: question or discussion Question or discussion
Projects
None yet
Development

No branches or pull requests

4 participants