diff --git a/blog/_posts/2019-03-16-spas-pwas-and-ssr.md b/blog/_posts/2019-03-16-spas-pwas-and-ssr.md new file mode 100755 index 0000000000..089f640364 --- /dev/null +++ b/blog/_posts/2019-03-16-spas-pwas-and-ssr.md @@ -0,0 +1,239 @@ +--- +layout: article +section: Blog +title: "SPAs, PWAs and SSR" +author: "Marco Otte-Witte" +github-handle: marcoow +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 one has to make a choice +to go with one of them. In this post we'll explore why that doesn't have to be +the case, since 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 that happen to +run in a browser as opposed to standalone apps. 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 + +![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: + +![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% respectively over 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 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, the big benefit of +the Single Page App approach 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 — SPAs trade a slow initial +load for fast or often immediate subsequent route changes. + +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. + +#### 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 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. + +#### 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. + +## 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 only some script tags and maybe a +loading spinner, the server would respond with the minimal set of code that is +necessary for rendering the app's minimal UI and making it interactive. 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 shell 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 really effective solution though for solving the problem of the +meaningless initial UI - be it an empty page, 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 for the initial request. + +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 for all routes the single page app + supports +* 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 app +* it then executes the app and renders the app's UI into a string, leveraging + libraries like [SimpleDOM](https://github.com/ember-fastboot/simple-dom) or + [jsdom](https://github.com/jsdom/jsdom) +* that string is then served as the response to the browser's initial request +* the pre-rendered response still contains all `