diff --git a/blog/_posts/2019-03-16-spas-pwas-and-ssr.md b/blog/_posts/2019-03-16-spas-pwas-and-ssr.md index b988d13ec0..3501a2e686 100755 --- a/blog/_posts/2019-03-16-spas-pwas-and-ssr.md +++ b/blog/_posts/2019-03-16-spas-pwas-and-ssr.md @@ -8,79 +8,194 @@ twitter-handle: marcoow topic: javascript --- -Single Page Apps, Progressive Web Apps and classic Server side rendered websites are often seen as orthogonal approaches to building web apps where only one is best suited for a particular project and a decision has to be made which approach to leverage early on in a project. In this post we'll explore why that doesn't have to be the case but all 3 approaches can actually be combined in order to achieve the best result. +Single Page Apps, Progressive Web Apps and classic Server side rendered +websites are often seen as orthogonal approaches to building web apps where +only one is best suited for a particular project and one has to decide which +approach to go with. In this post we'll explore why that doesn't have to be the +case but all 3 approaches can actually be combined in order to achieve the best +result. ## Desktop-grade apps -Modern websites are in many cases not really websites anymore but in fact full blown apps with desktop-grade feature sets and user experiences. While the much-loved [Spacejam Website](https://www.spacejam.com/archive/spacejam/movie/jam.htm) with its little blinking satellites for menu items was a pretty standard website only about 2 decades ago +Modern websites are in many cases not really websites anymore but in fact full +blown apps with desktop-grade feature sets and user experiences. While the +much-loved [Spacejam Website](https://www.spacejam.com/archive/spacejam/movie/jam.htm) +was a pretty standard page in terms of interactivity and design only about 2 +decades ago -![Video of the spacejam.com menu](/images/posts/2019-03-16-spas-pwas-and-ssr/spacejam.gif) +![Screenshot of the Spacejam Website](/images/posts/2019-03-16-spas-pwas-and-ssr/spacejam.png) -we can now go to [Google Maps](https://www.google.com/maps), zoom and rotate the earth in 3D space, measure distances between arbitrary points and have a look at our neighbor's backyard: +we can now go to [Google Maps](https://www.google.com/maps), zoom and rotate +the earth in 3D space, measure distances between arbitrary points and have a +look at our neighbor's backyard: ![Video of Google Maps](/images/posts/2019-03-16-spas-pwas-and-ssr/maps.gif) -All of this functionality, interactivity and visual appeal comes at a cost though, mainly in the the form of JavaScript code. The median size of JavaScript used on pages across the Internet is now [ca. 400KB on the desktop and ca. 360KB on mobile devices](https://httparchive.org/reports/state-of-javascript), an increase of ca. 36% and ca. 50% over the course of the past 3 years. All of this JavaScript not only has to be loaded via often spotty connections but also parsed, compiled and executed - often on devices that are less powerful than one might expect (have a look at [Addy Osmani's in-depth post on the matter](https://medium.com/@addyosmani/the-cost-of-javascript-in-2018-7d8950fbb5d4)). - -What all this leads to is that for many of these highly-interactive, feature-rich and shiny apps that are being built today, the first impression that the user gets will often be this: +All of this functionality, interactivity and visual appeal comes at a cost +though, mainly in the the form of JavaScript code. The median size of +JavaScript used on pages across the Internet is now +[ca. 400KB on the desktop and ca. 360KB on mobile devices](https://httparchive.org/reports/state-of-javascript), +an increase of ca. 36% and ca. 50% respectively over the past 3 years. All of +this JavaScript not only has to be loaded via often spotty connections though +but also parsed, compiled and executed - often on mobile devices that are far +less powerful than the average desktop or notebook computer (have a look at +[Addy Osmani's in-depth post on the matter](https://medium.com/@addyosmani/the-cost-of-javascript-in-2018-7d8950fbb5d4) +for more details). + +What all this leads to is that for many of these highly-interactive, +feature-rich and shiny apps that are being built today, the first impression +that users get is often this: ![Video of a loading JS app](/images/posts/2019-03-16-spas-pwas-and-ssr/loading.gif) ## SPAs -While JavaScript-heavy apps can be slow to start initially though, the benefit of the Single Page App approach that is most often followed to build them is that once the the app has started up in the browser, it is running continuously and handles route changes in the browser so that no subsequent page loads are necessary. In that sense the SPA approach trades a slow initial load for fast or often immediated subsequent loads. +While JavaScript-heavy apps can be slow to start initially, the big benefit of +the Single Page App is that once the application has started up in the browser, +it is running continuously and handles route changes in the browser so that no +subsequent page loads are necessary — the SPA approach trades a slow +initial load for fast or often immediate subsequent route changes. -The initial response though that the server sends will often be either empty or just a basic loading state as shown above. Only after the application's JavaScript has been loaded, parsed, compiled and executed, can the application start up. This results in relatively slow time to first meaningful paint and time to interactive metrics. +The initial response though that delivers the user's first impression will +often be either empty or just a basic loading state as shown above. Only after +the application's JavaScript has been loaded, parsed, compiled and executed, +can the application start up and render anything meaningful on the screen. This +results in relatively slow _time to first meaningful paint_ (TTFMP) and _time +to interactive_ (TTI) metrics. -### Time to first meaningful paint +#### TTFMP: Time to first meaningful paint -This is the time when the browser can first paint any **meaningful** content on the screen. While the time to first paint metric would just measure the first time **anything** is painted (which would be when the loading spinner is painted in the above example), for an SPA time to first meaningful paint would only occur once the app has started and the actual UI is painted on the screen. +This is the time when the browser can first paint any **meaningful** content on +the screen. While the time to first paint metric simply measures the first time +**anything** is painted (which would be when the loading spinner is painted in +the above example), for an SPA the time to first meaningful paint only occurs +once the app has started and the actual UI is painted on the screen. -### Time to interactive +#### TTI: Time to interactive -This is the time when the app is first usable and able to react to user inputs. In the above example of the SPA, time to interactive and time to first meaningful paint happen at the same time which is when the app has fully started up, has painted the UI on screen and is waiting for user input. +This is the time when the app is first usable and able to react to user inputs. +In the above example of the SPA, time to interactive and time to first +meaningful paint happen at the same time which is when the app has fully +started up, has painted the UI on screen and is waiting for user input. ## The App Shell Model -One popular approach for improving the startup experience of JavaScript-heavy applications, is called the [App Shell Model](https://developers.google.com/web/fundamentals/architecture/app-shell). The idea behind this concept is that instead of responding to the user's first request with an empty HTML document with maybe only a loading spinner and a number of script tags, the server would respond with the minimal set of code that is necessary for rendering and making interactive the app's minimal UI. In most cases, that would be a visual framework for the app's main blocks and some barebones functionality associated to that (e.g. a working slideout menu without the individual items actually being functional). - -Although this does not improve the app's time to first meaningful paint, at least it gives the user a first visual impression of what the app will look like (and users might even be able to develop an idea for what it will work like) once it has started up. Of course the app shall can be cached in the browser using a service worker so that for subsequent visits it can be served from that instantly. +One popular approach for improving the startup experience of JavaScript-heavy +applications is called the +[App Shell Model](https://developers.google.com/web/fundamentals/architecture/app-shell). +The idea behind this concept is that instead of responding to the user's first +request with an empty HTML document with only some script tags and maybe only a +loading spinner, the server would respond with the minimal set of code that is +necessary for rendering and making interactive the app's minimal UI. In most +cases, that would be the visual framework of the app's main blocks and some +barebones functionality associated to that (e.g. a working slideout menu +without the individual menu items actually being functional). + +Although this does not improve the app's TTFMP or TTI, at least it gives the +user a first visual impression of what the app will look like once it has +started up. Of course the app shall can be cached in the browser using a +service worker so that for subsequent visits it can be served from that +instantly. ## Back to SSR -The only effective solution for solving the problem of the meaningless initial response - be it an empty HTML document with script tags only, a loading indicator or an app shell - is to leverage server-side rendering and respond with the full UI or something that's close to it. - -Of course it wouldn't be advisable to go back to classic server-side rendered websites completely, dropping all of the benefits that single page apps come with (instance page transitions once the app has started, rich user interfaces that would be almost impossible to build with server side rendering, etc.) A better approach is to run the same single page app that is shipped to the browser on the server side as well as follows: - -* the server responds to `GET` requests to all of the routes the single page app defines and that would be transitioned to instantly once the app has started up in the browser -* once a request comes in, the server constructs an application state from the request path and potentially additional data like a session cookie and injects that into the SPA -* it then executes the SPA, including data loading and performs an initial render of the app's UI into a string, leveraging a library like SimpleDOM -* that string is then served as the response to the browser's request as opposed to an empty document with script tags only or an app shell -* the pre-rendered response of course still contains all `