Skip to content
This repository has been archived by the owner on Jan 11, 2023. It is now read-only.

Initial rendering remove all nodes from server side rendering and then render them again #911

Closed
hronro opened this issue Sep 24, 2019 · 13 comments

Comments

@hronro
Copy link

hronro commented Sep 24, 2019

Describe the bug
When load the page in the first time or click the "refresh" button in browser, the client side js is going to remove all html nodes rendered in server side, and then render them again.

To Reproduce

  1. Use sapper's official example (npx degit "sveltejs/sapper-template#rollup" my-app)
  2. Run the example
  3. Open localhost:3000
  4. Add a breakpoint of body element in Chrome dev tools
  5. Refresh the page
    2019-09-24 14-17-52 2019-09-24 14_27_47

Expected behavior
Sapper shouldn't remove those html nodes in first time rendering.

Information about your Sapper Installation:

  • Your browser and the version: Chrome 79

  • Your operating system: macOS 10.15

  • Your hosting environment: Local

  • Sapper version: 0.27.9

  • Svelte version: 3.12.1

  • If it is an exported (npm run export) or dynamic application: dynamic

  • Whether your application uses Webpack or Rollup: Rollup

@pngwn
Copy link
Member

pngwn commented Nov 19, 2019

Is this a bug with Sapper or a problem with Svelte's hydration?

@buhrmi
Copy link

buhrmi commented Jan 5, 2020

Why is this a problem at all? It's documented behavior.

@akaufmann
Copy link

akaufmann commented Jan 5, 2020

That should not an expected behaviour because removing all DOM nodes inside your APP node on the client after SSR should not happen. What else would be the advantage of SSR? It is as if you were using CSR. Hydration is okay.

@buhrmi
Copy link

buhrmi commented Jan 6, 2020

The main reason for doing SSR is that the time for the first meaningful paint is lower and that it's easier for search engines to crawl and index your website. Recreating the DOM client-side doesn't impact this at all.

@pngwn
Copy link
Member

pngwn commented Jan 6, 2020

If this is happening, then it is definitely a bug. Removing everything and recreating it is wasteful and not how SSR is supposed to work. There are benefits to delivering static-content even without hydration but it is this hydration process that makes it a powerful pattern, despite the problems it poses.

@buhrmi
Copy link

buhrmi commented Jan 6, 2020

How is it a bug? It's right there in the documentation:

Whereas children of target are normally left alone, hydrate: true will cause any children to be removed.

@pngwn
Copy link
Member

pngwn commented Jan 6, 2020

Because Svelte 'claims' nodes as long as the structures of the static version and dynamic version match, if these two versions of the site do not match then it will indeed bail and destroy them. Here is the relevant runtime code.

This distinction is mentioned in the docs because without hydration Svelte appends (ignoring other DOM), with hydration Svelte forces the contents to match whatever is inside you Svelte app. This is discussed in the docs to prevent duplicate DOM when building a static version and to prevent accidental removal when hydrating.

@mdempsky
Copy link
Contributor

mdempsky commented Jan 6, 2020

When re-hydrating a component, Svelte "claims" all of the child nodes that are still needed/wanted; removes all of the leftover unwanted nodes; and then re-appends the needed nodes to ensure they're in the correct order.

The flickering is because when breaking on DOM modifications, foo.appendChild(bar); is treated as first removing bar from the DOM and then re-inserting it into the DOM at the end of foo's child list. The debugger is exposing this intermediate step within appendChild, even though from the JS code's perspective it's an atomic operation.

The other aspect is that if you have a component X with children [A, B, C], because the component is mounted by re-appending the children, you'll visually see the nodes cycle around:

X.appendChild(A) => [B, C, A]  (A moved to the end)
X.appendChild(B) => [C, A, B]  (B moved to the end)
X.appendChild(C) => [A, B, C]  (C moved to the end)

This is a consequence of using the same generic mount implementation for both the rehydration process (i.e., after claiming) and for the normal component construction lifecycle (i.e., after createing).

I don't know enough about the browser paint cycle to know if this could ever actually be visible to a normal user (i.e., outside of debugging). But "fixing" it would probably require changing the Svelte compiler to emit a call like setChildren(X, [A, B, C]), which can then more intelligently check that X's children are exactly those three nodes in that order, and elide DOM operations when they're not necessary.

@ShynRou
Copy link

ShynRou commented Mar 10, 2020

I don't know enough about the browser paint cycle to know if this could ever actually be visible to a normal user (i.e., outside of debugging).

In my case the javascript engages about a second after site load, if a user enters something into the auto focused input field in that second, it is cleared by this and looses focus.
Svelte should actually just take possession of the existing elements.

@frederikhors
Copy link

@pngwn can I ask you why the pending clarification tag on this issue?

I think this is really a bug and maybe the correct tag could increase its resolution priority. Am I wrong?

Has anyone found a way to get around in the meantime?

@frederikhors
Copy link

frederikhors commented Apr 30, 2020

Here is a gif for you.

See the Date.now() value and the green re-painting:

123456

@frederikhors
Copy link

This is particularly a problem when you have animations (even just css) on the elements as soon as they are rendered.

@benmccann
Copy link
Member

There are a number of issues tracking this over in the Svelte repo with a list of some of them in sveltejs/svelte#4975. I agree this could be improved, but it has to be addressed in Svelte, so I'm going to close this one in favor of the Svelte issues

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

9 participants