In this post, I’d like to discuss the option we have to accommodate using client-side rendering frameworks (e.g., Vue, React, Web Components…) while preserving essential properties such as fast page load, accessibility, support for older browsers or good indexation by search engines.
This is a follow-up to my post on the impact of modern front-end technologies on non-functional properties, addressing the most technical aspect of the discussion.
Glossary
First, the definition of some acronyms that we’ll use frequently in the rest of the discussion.
- SPA: An SPA (Single-page application) is a web app implementation that loads only a single web document, and then updates the body content of that single document via JavaScript APIs such as Fetch when different content is to be shown.
- SSR: Server-side rendering (SSR) refers to the practice of generating HTML content on the server and sending it to the client. SSR is opposed to client-side rendering, where the client generates the HTML content using JavaScript. Both techniques are not mutually exclusive and can be used together in the same application.
- SE: A search engine is a software system that collects information from the World Wide Web and presents it to users who are looking for specific information.
Options
See below the list of options I considered so far. Please tell me if you think some other approach is worth considering.
Note that this discussion must be though from XWiki and Cristal perspective.
1.1/ Server-Side Rendering — Embedded
With Server-Side Rendering (SSR)[1], the server can evaluate the same template language as the client.
This allows to precompute the HTML before returning a page.
Therefore, even without JavaScript, a complete HTML page is returned to the client.
A client with JavaScript can then “hydrate” the page with additional content and interactivity, but the initial content is already enough to have a usable web page.
In the case of an SPA, further navigation is done purely client-side with asynchronous Ajax requests.
Pros:
- fast initial page load speed and SE
Cons:
- The template engine must be interpretable server-side. Which usually means being able to run a JavaScript interpreter, which does not pair well with our Java backend.[2]
- This is also tightly binding the front-end app to the backend, which is not always desirable, in particular in the context of Cristal where we don’t always control the backend capabilities.
1.2/ Server-Side Rendering — Third Party
This case is close to 1.1, but the rendering is done by an independent Node.js server, responsible for rendering the SSR pages.
Pros:
- easier to implement as this is natively supporting JavaScript for template rendering
- no tight binding to a specific backend, making it a good fit for Cristal
Cons:
- one more server to deploy and maintain, plus the need to share credentials etc
- risk of latency when fetching data from the “backend” server (e.g., XWiki) to the “SSR” server over http
2/ Progressive Enhancement — Throwaway HTML
In this case, a version of the page with essential content (e.g., body, navigation tree, comments, etc.) is rendered so that users without JavaScript, or search engine indexers, get an initial content.
But, this content is discarded as soon as the JavaScript is loaded and running.
The main difference with the SSR option is that here the logic is duplicated, with dedicated templates written using a different templating language interpretable using Java (e.g., Velocity, Thymeleaf) being used.
Pros:
- Good for performance and SE
Cons:
- manual work and maintenance
- larger resource consumption and network use, as the computed HTML content is not used afterward. The impact can be limited in the case of SPA, as this additional computation will only happen once for every full-page load, the rest of page rendering being done client side.
3/ Precomputing of data
In this option, no rendering is done server-side.
The main point is to precompute the data required for the first rendering directly during the initial response.
While this does not avoid the client having JavaScript to be able to get the UI, all the asynchronous requests required to render the full page as not needed anymore are they have been precomputed.
Once again, in the case of an SPA, the navigations after the first page load are done client side using Ajax queries.
Pros:
- some code duplication is required, but probably less than in option 2, as we only need to aggregate the data without rendering them
Cons:
- bound to the backend capabilities, so not always a good fit for Cristal
- still a risk for SE, as JavaScript is required to actually render the page (the rendering is just faster since no asynchronous requests are required anymore on the first page load).
Conclusion
What do you think of the options proposed above?
Do you think some other options should be added to this list?
Thanks
Vue’s documentation: Server-Side Rendering (SSR) | Vue.js ↩︎
At the exception to experimental projects such as GraalVM/Polyglot or GitHub - caoccao/Javet: Javet is Java + V8 (JAVa + V + EighT). It is an awesome way of embedding Node.js and V8 in Java. ↩︎