Hacker News new | ask | show | jobs
by mxmxnxor 1496 days ago
>Rendering HTML server-side is just nice

No, rendering html server-side is not nice and never was. Why on earth you will send markup template for every list item over and over again instead of sending just data and only one code-template (to render data on a client) ? Suppose you need to show to user a list of books on your web-page. Server side rendering means you need to send to user a markup consists of html elements for every book

<div> <div class="book"> <div class="book-title">Book 1</div> <div class="book-author">Author 1</div> <div class="book-description">..description..</div> </div> <div class="book"> <div class="book-title">Book 2</div> <div class="book-author">Author 1</div> <div class="book-description">..description..</div> </div> <div class="book"> <div class="book-title">Book 2</div> <div class="book-author">Author 2</div> <div class="book-description">..description..</div> </div> </div> ...

Don't you see something wrong with this? Why you need to duplicate over and over again html template for every list item resulting of significant increase of size of page to download if you can send only one template-component like this

<div> <div class="book"> <div class="book-title">{book.title}</div> <div class="book-author">{book.author}</div> <div class="book-description">{book.description}</div> </div> </div>

and a data in a more compact way

[{title: "Book 1", author: "Author 1", description: "..."}, {title: "Book 1", author: "Author 1", description: "..."}, ...]

or more compact like this

[["Book 1", "Author 1", "..."],["Book 1", "Author 1", "..."], ...]

or even in more compact binary-encoded format resulting in more than magnitude size compaction comparing to over-duplication html-approach. More size means more traffic for users to download over network and users which use data-roaming will "thank" you for your hundreds of kilobytes instead of dozens

This is why server-side rendering approach was flawed from the beginning (not because of page reloading but because of data duplication and traffic consumption)

7 comments

This is wrong because you’re not considering the whole picture:

- to turn the JSON into HTML, you not only need the template you mentioned, you also need code to execute that template (potentially lots of it); preact is 10KB ungzipped (other frameworks, like React/Angular/Ember, are way larger), so your HTML solution is already 10,000 characters ahead (by comparison, the markup you demoed is ~90 bytes per book, so you’re ahead until at least ~100 books on the page, without considering the framework, your code, and the following points)

- HTML compresses amazingly well because it’s repetitive so the overhead is less than you think

- HTML stream renders, so you can start rendering the first book immediately; JSON streaming is technically possible, but not built-in, and quite difficult to implement (and it requires even more JS)

- the same KB of JS is way more expensive than it would be as HTML (or images) because parsing, compiling, and executing is slow

- latency is often a larger problem than bandwidth, especially on mobile, so saving bandwidth is less important than (a) streaming and (b) client-side blocking time

- a large number of users visit one page and bounce, for typical websites, so the promised savings once the SPA is live doesn’t materialize

- the naive way to implement a SPA is _full_ of footguns; first of all, you’d naively load the template for all the pages, making the overhead problem even worse, naively you wouldn’t render anything until the JS has arrived, delaying the TTFMP enormously, naively you’d mess up routing, etc.

- it’s relatively trivial to preload/precache HTML pages, including in a service worker to support offline, making performance on par or better than a SPA

I happily work on a huge SPA daily, for context.

- about HTTP compression (gzip, brotli) - it works on byte/text level and doesn't know about your markup template repetition (where structure is mostly the same but small changes everywhere) so it will be not as efficient as manual template-data separation

- about SPA - in my comment I said no word about SPA, it's a more complex layer on top. I only arguing about template-data separation approach to decrease data duplication and traffic consumption. And no, you don't need to rewrite your app as SPA and no react/preact/javascript frameworks required. You can use your server-side rendering and just change format of what you are sending to user. Instead of sending index.html with repetitive html markup for every list item you can basically send script-tag where you loop over data and build html-markup directly on a client

<doctype html>

<html>

<body>

   ....

   <div id="books"></div>

   <script>

     const data = [{title: "...", author: "...", description: ""}, {...}]

     document.querySelector("#books").innerHtml = data.map(item=>`

        <div class="book">

           <div class="book-title">${item.title}</div>

           <div class="book-author">${item.author}</div>

           <div class="book-description">${item.description}</div>

        </div>

     `).join(``);

   </script>

 </body>
</html>

Yes, it lacks http streaming but - less repetitive html markup -> smaller size -> less time to download -> http streaming becoming less useful

And again - you can use your favorite http compression (gzip, brotli, etc) on top of this template-data separation approach to compress even more

I’ve just tested what you suggest with a page that contains 20 books (no HTML or JS minification, and probably some typos because I didn’t run it).

https://pastebin.com/8CM1eUKQ the HTML version https://pastebin.com/ujrai58n the templated version

Even I was surprised by the result. The original size is what you’d expect: 5.3K for the full HTML version, 2.4K for the JS template version. Once Brotlied (default compression) however, the situation changes completely: 127 bytes (!) for the pure HTML version and 197 bytes for the template version. This is even true for Gzip, 213 vs 284 bytes.

I don’t know if Brotli (and Gzip) are somehow optimized for HTML, but yeah it really doesn’t make sense to use templates here.

> Why on earth you will send markup template for every list item over and over again instead of sending just data and only one code-template (to render data on a client)?

I see what you're saying, but I think server-side rendering is simpler, and the concern about repeated HTML tags in the content is likely much easier to address by using gzip over the wire.

Not necessarily, because you have to consider the entire lifecycle.

When doing frontend rendering you usually work against a REST(ful) API to fetch data, the idea is to reuse endpoints for multiple frontend components, because of this generalization you usually end up sending more data than necessary, not only between the database and the backend, but also between the backend and the frontend.

And not only that, you start doing JOINs over HTTP and JavaScript.

With server side rendering I can write an exact SQL query to render that component.

Ah ... calling server-side "flawed from the beginning" is a bit overkill. The use case is a bit convoluted. HTML itself is not the problem that slows down modern web experience, is it?

Also, in your example, don't forget the code required to render that data, which is often a second fetch and is, you guessed it, utf-8 / ascii/ unicode. Now we've double startup costs or forced http/2.

There's some context with these opinions. I concur that rendering server-side is often much, much simpler, but acknowledge that might be because that was what came first; and I acknowledge that some of the hate against client-side is b/c modern web often feels bloated and JS takes the blame.

> Why on earth you will send markup template for every list item over and over again instead of sending just data and only one code-template (to render data on a client) ?

You have a point, but not everything needs to be black and white. We had this nifty technology called AJAX even before SPAs were a thing. Now I understand that SPAs are built on AJAX calls too, but it is also possible to render (most of the) HTML on the server side and only use AJAX using off-the-shelf components such as Datatables when you absolutely need that functionality.

Two words: HTTP compression
HTTP compression (gzip, brotli) works on byte/text level and doesn't know about your markup template repetition (where structure is mostly the same but small changes everywhrere) so it will be not as efficient as manual template-data separation. Moreover you can also use gzip, brotli for your template and data so http compression doesn't change the overall picture
It changes the overall picture because HTML compresses better than JSON, which itself compresses better than your deduped arrays (because of the duplication you’re trying to avoid). So the HTML would be closer in byte size to the most compressed version you’ve shown than it appears, after compression.
^^^ this. Plus you’re probably going to have to render the JSON you send on the server and then rerender it as HTML on the client plus send the code amd template to do that too.

Also I know there’s the promise of sharing code between the front and backend but the reality is that you’re probably more likely to reimplement one or the other and then have to keep them in sync.

The overhead for doing it just the once on the backend and sending compressed HTML over the wire is pretty low for a small team in comparison.

a third word: DIVitis.

but yeah, gzip or the more recent ones such as brotli have been taking care of those things for 25 years....

so HTML server-side has always been nice.

Especially semantic markup cough cough

Do you even gzip transfer-encoding?