-
-
Notifications
You must be signed in to change notification settings - Fork 52
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
Pre-rendering Laminar pages [SSR] #60
Comments
There was some gitter discussion about this, linked in #46. Basically I agree, headless chrome is the way to go. And you're right, hydration is much harder in Laminar than React. Architecturally, for hydration React can just apply the same diffing logic as before, it just needs to read previous state from the real DOM rather than from the previous virtual element. In Laminar on the other hand, the problem is that modifiers can contain arbitrary executable instructions, not just key-value pairs, and modifiers don't have any built-in reconciliation logic.
So, bottom line, I don't think true virtual DOM style hydration will be possible in Laminar. However, as you said, we can achieve much of the same by pre-rendering the page and on page load simply replacing the contents of the app container with the live Laminar app. Async stuff like ajax requests aside, the initial rendering should happen synchronously as soon as the DOM becomes available, so at first glance I think performance would be the only concern. Other potential concerns would be – what if the user starts to interact with the website before we load the interactive Laminar app, like, what if they focus on an element and start typing into it. But I think the browser should be blocked while loading the live Laminar app, as it always is while executing synchronous Javascript. In terms of performance, the browser will do the unnecessary work of parsing a bunch of HTML and instantiating all those elements. But the elements would be inert, with no subscriptions defined on them, so it should be slightly less work than initializing the real Laminar app. For the vast majority of applications I don't think the difference will be noticeable – even if it's close to 2x increase, the base time is usually so small. Much of real life loading time is spent parsing JS, loading resources, etc. rather than initializing the DOM. If you only care about this for SEO, you can further improve this by not serving the JS to google bot and by not serving the HTML to real users. I assume google won't punish for such antics but I haven't checked. What kind of data are you planning to save in json? Stuff like ajax responses? That would be another way to improve performance where the data is not user-specific. Overall I think this is a legit approach, I haven't done it myself yet but planning to try it out eventually. If any of what you've done is shareable as a gist or a blog post, quite a few people would appreciate I think. |
Yeah realistically the pages would have to be parameterized to hide/disable inputs during pre-render.
I mostly agree. I am just somewhat worried about big pages (e.g. >5k elements) on mobile devices. Double render might have some impact in that case. I am still trying to verify this.
Yeah, our use case is similar to a classic e-commerce site example where you have lots of public pages with same layout but different content. So client side navigation (after the first load) would just load the content from those json files rather than loading prerendered html on each navigation, or querying the backend. I am basically trying to accomplish what Next.js does (that's an interesting read about current state of the art of serving things fast btw) with their static generation / hydration.
At this point I am just running puppeteer manually to dump html and overwriting stub page with that, but if I get something more advanced going, I will definitely share. Thanks for your inputs! |
Sounds about right. Good article about Next.js, thanks. By the way, make sure to try your scala.js app with es2015 output disabled on mobile safari. In at least a couple ScalaJS-React apps es2015 output is causing a massive slowdown in parsing of the application bundle, with the browser taking several seconds to parse a 1mb bundle. I looked and couldn't find any evidence that Mobile Safari is in general slow to parse es2015 so it must be something specific to the Scala.js es2015 output that it doesn't like. |
Hey. Was wondering graalvm can also help in the ssr runtime debate. Seeing some articles with people evaluating the js code using graalvms polyglot features with relatively good performance to node. |
Maybe, if you can make graalvm run jsdom. Running it on a node.js server seems simpler and a more travelled path (not for Laminar but in general), but I guess both could work, just a matter of which runtime you're more familiar with. |
I am looking for options to pre-render Laminar pages, and would like to see if there is any prior art or best practices.
My current hypothetical plan is as follows:
I did some preliminary testing of this scheme, and it appears to be working. My main concern is the lack of hydration support in Laminar, which necessitates double render on first hit. Are there any plans to add hydration support to Laminar? It seems hard - this seems like one of the few areas where DOM diffing has advantage. I am also not sure how much double render matters in practice - hydrating isn't exactly cheap either.
Any thoughts on what is the best way to do this?
The text was updated successfully, but these errors were encountered: