Closed avi closed 3 years ago
If anyone else is blocked by the iframes ssr/hydration problems or really cares about hydration speed, I'm currently maintaining a fork https://github.com/wix-incubator/wix-svelte which you can use directly just point your package.json "svelte": "ssh://git@github.com:wix-incubator/wix-svelte.git#master" (or hard code the current sha1 if you don't want to get updates)
At the moment this is a major problem on a website I'm working on with Sapper. I opened an issue on Sapper repo. https://github.com/sveltejs/sapper/issues/1088
(@avi I'll try your fork tomorrow and let you know if it worked)
@avi I just tested your fork on my project (see commit https://github.com/sandrina-p/s008080_2019/commit/96cfa060516676315aa74b168c9e2385275f6d57) and it worked! Thanks! 😻
I'd like to see this improvement absolutely. Currently we're server-side rendering components via node CLI, then hydrating on the client (building a language learning app). Because SSR elements are cleared and re-added it results in the rendered page being displayed, before disappearing to be replaced with a loading message, and then the hydrated page appears.
Just found this issue after looking around. +1 for this!
My specific issue related to this is that it causes Largest Contentful Paint to only be registered after hydration is done, because it removes the DOM node and re-insert it.
Sorry this PR probably needs to be redone, we've postponed using svelte for now. But I'll try to do it later this week, if someone tells me whether to do it as a compiler flag or a breaking change.
On Tue, Jun 16, 2020 at 12:42 PM Jacky Efendi notifications@github.com wrote:
Just found this issue after looking around. +1 for this!
My specific issue related to this is that it causes Largest Contentful Paint https://web.dev/lcp/ to only be registered after hydration is done, because it removes the DOM node and re-insert it.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/sveltejs/svelte/issues/4308#issuecomment-644656508, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAAQWIOLLPBTVGDH6H3ZCC3RW45ATANCNFSM4KKUK5TQ .
@avi thanks for taking another look at this!
I had a question on overall approach. Take a look at https://github.com/sveltejs/svelte/issues/4975 and let me know what you think
The problem is that this a breaking change right now and we need to decide if the old way is worth preserving to make migration easier or not. I think it isn’t because right now the diff between doing hydration and cleaning all the SSRed DOM and doing client side render is better than using hydration as is... but that is easy for me to say because we don’t use svelte right now partly because of this issue... I need a decision/guidance from a maintainer...
the diff between doing hydration and cleaning all the SSRed DOM and doing client side render is better than using hydration as is...
This is a deal-breaker for websites that have animations or media autoplay (e.g. audio, videos, gif, etc...) on page load because it would restart them when the CSR is done, as I explained in the issue https://github.com/sveltejs/sapper/issues/1088.
For what it's worth, I'm leaning towards the idea that client-side rendering (CSR) with a CDN that pushes preload
assets and dynamic rendering is a better solution in nearly all cases than SSR + hydration. See https://github.com/sveltejs/sapper/issues/383#issuecomment-642842357 and https://developers.google.com/web/updates/2019/02/rendering-on-the-web for more details. I've really been pushing on that path in Sapper with https://github.com/sveltejs/sapper/pull/1269 and https://github.com/sveltejs/sapper/pull/1275. I might be almost as happy to remove hydration altogether because it seems like a trap that's easy to fall into. Intuitively it makes sense, but I'm not sure it actually has any benefits over the CSR + CDN + dynamic rendering solution, which hasn't received as much attention - though happy to be corrected if I'm overlooking something or explain further if more details would be helpful
The biggest advantage of hydration is SEO/no-script support if these aren't important for your use cases a preload + client side rendering might be better. If you only care about FMP/LCP/TTI it would depend on the sizes of the bundles vs resources vs HTML, and that would depend on the specifics of the project. You can also probably write this a generic solution for this approach of preloading assets, which uses a special variant of SSR compiler that only generates the preload links by finding all resources pointed to in the HTML. You don't even have to parse HTML strings, except for @html content and maybe those can be delivered as phase2.
This being a breaking change isn't just some theoretical concern - I have a project right now where there's some text related to timezones that might get changed during hydration vs what came from the server. If we want to have something that makes more assumptions about what it's starting with, it would need to be opt-in rather than a default, and ideally it would also be something over which more control can be exerted at compile time, as no one knows better than the application author which bits are safe to assume will already match. I don't know what that new API or component syntax would look like.
Text node diffs were handled in the PR - but DOM attributes were not - if the class of an element is different it would change the current behaviour as the current behaviour is to remove all attributes from every element claimed and start writing back to them. I know it is a breaking change, but I believe that people for which this is a breaking change would be better off by removing all the DOM they got from SSR, and doing client side render, as it will give better performance/smaller bundle size, as the affect of removing all attributes from elements is that everything is recalculated in the browser every element is tainted and the browser needs to do more work than it does in a client side render because elements are removed/reattached to the DOM constantly.
On Tue, Jun 16, 2020 at 9:22 PM Conduitry notifications@github.com wrote:
This being a breaking change isn't just some theoretical concern - I have a project right now where there's some text related to timezones that might get changed during hydration vs what came from the server. If we want to have something that makes more assumptions about what it's starting with, it would need to be opt-in rather than a default, and ideally it would also be something over which more control can be exerted at compile time, as no one knows better than the application author which bits are safe to assume will already match. I don't know what that new API or component syntax would look like.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/sveltejs/svelte/issues/4308#issuecomment-644930732, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAAQWIJYF5DDJRDROQLCBADRW6Z5DANCNFSM4KKUK5TQ .
If I'm not mistaken, in React, when the SSR (server-side render) and CSR (client-side render) don't match, there's a warning in the console about it. Maybe here it could follow the same approach and explain that those types of "diffs" must be fixed.
Example: Check the URL to activate an element (by adding a class active
). This will cause a diff between SSR and CSR because window
does not exist in the server. So, the solution is to only check the URL after onMount()
. That way both SSR and CSR will match.
I got a bit more detail from the other maintainers on why this PR has languished. There's an overall feeling that the current approach of repairing the DOM is considered to be a feature not a bug. There are cases it handles that the PR would not such as progressive enhancement of a select
that turns into an autocomplete upon client render. There's also a recognition that the current implementation has performance concerns and edge cases it doesn't handle well (iframes, videos, etc.) and leaves much to be desired. However, I think there's some feeling that we should investigate whether it's possible to create a solution that addresses these shortcoming without losing out on the existing DOM repairing feature.
I know there's been a lack of communication on this PR. We've tried to make a handful of changes to make it easier to discuss changes lately such as setting up #svelte-dev
and #contributing
channels on Discord and better surfacing the RFC process. The hope is that we can discuss any big changes before they get to the implementation stage to work out the details in the most efficient manner, so please feel to free to find us via those avenues.
I don’t mind implementing it as a way to kickstart the discussion unfortunately the discussion part was lacking. I suggested adding the alternative implementation as a compiler flag so users opt-in/out of the new behavior, it would also have been possible to change it so only a specific subtree is repaired for specific cases such as progressive enchantment.
In the meantime we have unfortunately postponed adopting svelte for the current rewrite, not only as a runtime but also as an authoring method because of the typescript support which has been fixed now ( played with compiling svelte to react components so we can work around the hydration problems until this issue is resolved )
I know all the maintainers are busy and have full time jobs other than svelte so I am understanding of the situation, but I feel this was a missed opportunity for the project, as we were looking into investing significant resources to improve the ecosystem
I still hope to use svelte in a future project :)
On Sep 21, 2020, at 00:03, Ben McCann notifications@github.com wrote:
I got a bit more detail from the other maintainers on why this PR has languished. There's an overall feeling that the current approach of repairing the DOM is considered to be a feature not a bug. There are cases it handles that the PR would not such as progressive enhancement of a select that turns into an autocomplete upon client render. There's also a recognition that the current implementation has performance concerns and edge cases it doesn't handle well (iframes, videos, etc.) and leaves much to be desired. However, I think there's some feeling that we should investigate whether it's possible to create a solution that addresses these shortcoming without losing out on the existing DOM repairing feature.
I know there's been a lack of communication on this PR. We've tried to make a handful of changes to make it easier to discuss changes lately such as setting up #svelte-dev and #contributing channels on Discord and better surfacing the RFC process. The hope is that we can discuss any big changes before they get to the implementation stage to work out the details in the most efficient manner, so please feel to free to find us via those avenues.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or unsubscribe.
Hey, since the topic of this issue is 'Better hydration' and maintainers seem to be investigating the subject, I'll drop my 2 cents on a somewhat perpendicular concern... ^^ If it diverges too much from the original discussion, I am glad to take this discussion somewhere else.
It would be nice to have a way to target parts of the DOM for hydration more precisely. For the moment, we can only wipe out everything inside a DOM node. (I am currently using an alternative implementation that replaces the node I need to hydrate for convenience.)
The use case is partial hydration of a component containing multiple top-level elements (that may be side by side with other elements from other components). Partial hydration is a feature that is on everyone's mind when looking at the future of static site generators (SSG) or server side rendering. I am working on an SSG and I have the feature working but it comes with compromises that I would love to be able to avoid, namely the whole component that we want to hydrate with JS needs to be wrapped in an HTML element alone. While it does not seem that big of a deal, it is the kind of things the dev needs to understand, watch for and keep in mind at all times. We cannot assume that it 'just works' and pick and choose the parts we want to hydrate. And it is counter intuitive since Svelte allows us to create side-by-side elements in a component easily.
I have read a few issues regarding this behavior and understand the concern of shipping too much code when it only applies to edge use cases. I am posting this here because I am wondering if there is a better solution for hydrating that does not involve shipping more code. I don't know the ins and outs of hydration with Svelte so I am mainly talking about developer experience here.
I am happy to discuss hydration and provide examples of how I use it if it can help bring the discussion forward. :)
PS: Another SSG project doing partial hydration with Svelte is ElderJS. I haven't investigated yet if the author managed to workaround the limitations I mention.
Elder.js maintainer here.
You nailed it. Components must be wrapped in a named div so we can target it. The implementation breaks inline, inline-block components (icons for instance), grid components but most of can be fixed with css.
The greater problem we’ve faced is the behavior of the CSS being emitted during SSR. If there are any false-y values the css is just excluded.
While it makes sense to exclude css that is not relevant when rendering on the client, on the server it makes 0 sense.
We’re still trying to find a work around.
I don't want to get too far off-topic here, but Sapper's CSS handling was recently rewritten and we plan to refactor it out into a separate Rollup plugin in the hopefully not-too-distant future (I'm mainly waiting to have a release or two without any CSS bug reports to make sure things are really working well first). I'm not fully aware of what your issues are, but you might want to watch the Sapper release notes for when that happens. Feel free to ping me on Discord or elsewhere for more details, so that we don't take this off-topic here
After a couple of months working on my SSR+hydration Svelte project I've finally hit this.
Since I was working locally, the hydration hiccup was pretty much unnoticeable, until today I had some component with a CSS transition which was restarted during hydration.
Honestly, I'm not really worried about CSS transitions since I can always wait with onMount
to trigger those somehow. What worries me is that input fields lose focus after hydration and the issues described earlier about iframes, autoplaying media, etc.
It's weird because the docs state this when enabling the hydratable
setting on the compiler:
If true when generating DOM code, enables the hydrate: true runtime option, which allows a component to upgrade existing DOM rather than creating new DOM from scratch. When generating SSR code, this adds markers to
elements so that hydration knows which to replace.
This led me to believe (my fault) Svelte wouldn't replace the DOM nodes when hydrating, but it's exactly what seems to be happening. It's unfortunate the docs don't have more specific information about what happens during hydration, specially these caveats.
I've recently hit the exact same problem. Been tinkering a bit with the hydration code in my free time, and I think it should be possible to rewrite it so that elements are not re-inserted, without the breaking changes that the previous PR introduced.
This seems to significantly impact the LCP on our website, especially on mobile. Largest Contentful Paint (LCP) is a Core Web Vitals metric and will be a ranking factor in 2021.
For issues with iframes, safari picture element, etc its possible just provide raw (compiled) svelte component. With few fixes in create_fragment.
Gonna try after vacation, in a week.
...
function create_fragment(ctx) {
let picture;
let source;
return {
c() {
// If nodes found on hydration do not create them here use hydrated
},
l(nodes) {
// DO NOT DETACH NODES, JUST FIND THEM
},
m(target, anchor) {
// use existing tree if nodes found
},
...
};
}
For issues with iframes, safari picture element, etc its possible just provide raw (compiled) svelte component. With few fixes in create_fragment.
Gonna try after vacation, in a week.
... function create_fragment(ctx) { let picture; let source; return { c() { // If nodes found on hydration do not create them here use hydrated }, l(nodes) { // DO NOT DETACH NODES, JUST FIND THEM }, m(target, anchor) { // use existing tree if nodes found }, ... }; }
@istarkov I'm really interested in this solution, do you have a link to where you're doing this in an actual project?
Finally wasnt good idea :-) as you need to override the whole tree. Doesnt affect me for now, so I skipped
Ah, this was actually the same dead end I ran into. Haha, I was hoping you had a solution I didn't think of.
Ran into same issue, this has deterimental impact on performance for my webpage (loading many iframes!). Doing it client-side works fine, but preloading does not help with performance for iframes it seems https://stackoverflow.com/questions/32547844/how-to-preload-iframes
For what it's worth, I'm leaning towards the idea that client-side rendering (CSR) with a CDN that pushes
preload
assets and dynamic rendering is a better solution in nearly all cases than SSR + hydration. See sveltejs/sapper#383 (comment) and https://developers.google.com/web/updates/2019/02/rendering-on-the-web for more details. I've really been pushing on that path in Sapper with sveltejs/sapper#1269 and sveltejs/sapper#1275. I might be almost as happy to remove hydration altogether because it seems like a trap that's easy to fall into. Intuitively it makes sense, but I'm not sure it actually has any benefits over the CSR + CDN + dynamic rendering solution, which hasn't received as much attention - though happy to be corrected if I'm overlooking something or explain further if more details would be helpful
As a temporary solution until hydration is improved I currently disabled SSR+hydration in my project and switched to CSR. I just send the data into a <script type="application/json">
and the page component to the client. It works better than I expected. If you go back the state is maintained in forms, JS, etc. I haven't tested this thoroughly though. I suspect there will be issues.
Honestly, there should be huge red warnings in the docs about the current state of hydration in Svelte which seems almost unusable. Makes you wonder if that's the reason why Sapper pushed people into a SPA (and apparently SvelteKit will too).
Edit:
I was wrong. The JS state is not maintained when going back with CSR. The form values are though.
After a long discussion with Rich on Twitter about client-side routing, I have to concede the SSR+hydration approach is less desirable than I initially thought for my use case (backend dashboard).
I totally wish hydration was better in Svelte, but if using client-side routing this is definitely much less of an issue.
As a temporary solution until hydration is improved I currently disabled SSR+hydration in my project and switched to CSR. I just send the data into a
<script type="application/json">
and the page component to the client. It works better than I expected. If you go back the state is maintained in forms, JS, etc. I haven't tested this thoroughly though. I suspect there will be issues.Honestly, there should be huge red warnings in the docs about the current state of hydration in Svelte which seems almost unusable. Makes you wonder if that's the reason why Sapper pushed people into a SPA (and apparently SvelteKit will too).
Although a bit unrelated but having an option in sveltekit to switch between ssr and this method while preserving all the other features would be great.
Any open blockers on this?
Huge SEO priority as we’ve seen Google is very slow at indexing pages that have large chunks of their content hydrated.
To me this points to Gbot waiting for the JS rendering of the page to be done before analyzing the page for quality mainly because the DOM is destroyed and rebuilt. Yes this is speculation but very hard to understand why these pages would be taking so long to be indexed. It is a common theme across several sites.
To me this points to Gbot waiting for the JS rendering of the page to be done before analyzing the page for quality mainly because the DOM is destroyed and rebuilt. Yes this is speculation but very hard to understand why these pages would be taking so long to be indexed. It is a common theme across several sites.
Yeah GoogleBot definitely waits for JS execution. A couple of years ago I remember Google made a big deal out of GoogleBot finally being able to execute ES6.
There are services that pre-render sites to static HTML specifically for crawlers such as: https://prerender.io/
Google has actually an article with more info about this: https://developers.google.com/search/docs/guides/dynamic-rendering
I’m definitely familiar. The thing is that we’re SSRing (static exporting) the entire page and partially hydrating 3-4 components. (Elder.js)
If a component that is hydrated is above the fold Google delays indexing until the JS bot finishes rendering which takes days to weeks.
This isn’t the case with all SSR setups and I believe is unique to Svelte’s poor hydration model that throws out the existing DOM and rebuilds it.
Reopening this since the performance improvement had to be reverted (https://github.com/sveltejs/svelte/pull/6290) due to a bug. While reverting we added a test to prevent future regressions. We might be able to get some more tests in by fixing up and merging https://github.com/sveltejs/svelte/pull/4444
If anyone would like to take another stab at getting the performance improvement in, the issue with the original change is described in https://github.com/sveltejs/svelte/issues/6279#issuecomment-831097210 and we'd welcome a PR that improves performance while avoiding that issue.
@tanhauhau I just happened to stumble upon https://github.com/sveltejs/svelte/pull/3766 today where you said that you're merging consecutive text-a-like nodes. I think there's a good chance that would actually allow us to get the hydration performance fix back in as it was without any additional changes. It was failing when encountering consecutive text-nodes because they were represented as multiple nodes, but would be merged by the browser such that hydration would fail on the client because it was looking for multiple nodes, but they'd been merged. If we're able to get your PR in and we always treat them as a single text node then I think we wouldn't have this issue.
Sorry if this was answered elsewhere, but has "better hydration" been addressed or has it been abandoned?
@danawoodman its periodiocally added, then reverted, so its hard to maintain current state :-)
I believe the primary issue mentioned here (slowness and animation flicker) has been fixed since Svelte 3.38.3 with the #6395 PR. There seems to be some issues to iron out in some edge cases (There were some problems with @html
nodes, but I haven't had the time to look at how everything stands in the last couple of weeks) but most use cases should be fine with the current implementation. If your application has not improved (or has had a regression) please open an issue 😄
SSR aside of expensive is veeeery slower on my production app, so in the end I've opted to disable SSR globally to have an acceptable performance in my app
@eriknyk what version of Svelte are you using?
I did some SSR benchmarks recently with 3.58.0
and the SSR perf was quite good. Almost as good as Solid which is one of the fastest and even dedicated template engines like Handlebars and Art.
https://github.com/sveltejs/svelte/pull/9662 skips hydration for select attributes and adds a warning in dev mode if the SSR and client-side values are different. This is similar to the idea @avi implemented in https://github.com/sveltejs/svelte/pull/4309, but for a few attributes that are unlikely to differ rather than the entire DOM so that repair still occurs if there are differences elsewhere. Interested users may wish to test that PR or Svelte 5 pre-releases containing the PR after it's merged
Is your feature request related to a problem? Please describe. Currently hydration is very expensive, it searches for the correct node type clears it and re-adds the attributes and finally unmounts and remounts the DOM node by calling insertBefore/append This causes iframes to reload. In the current implementation setting the innerHTML to '' and doing client side render is faster and cleaner
Describe the solution you'd like All DOM should be reused, attributes should not be cleared, and no node should be insertBefore/append unless there is an actual reason. DOM nodes shouldn't be scanned for the correct nodeName - assume everything is in order on the first miss clear all the rest and start adding new nodes as needed Text nodes need special treatment as they collapse in SSR
Describe alternatives you've considered A clear and concise description of any alternative solutions or features you've considered.
How important is this feature to you? It is a deal breaker for us - we have quite a few iframes in use and using hydration in it's current form reloads them, it also slows our TTI. So we are stuck with either using only client-side rendering or picking a different solution
Additional context