|
1 | 1 | # Application Shell Architecture
|
2 | 2 |
|
3 |
| -A modern web application architecture leveraging [Service Worker](http://www.html5rocks.com/en/tutorials/service-worker/introduction/) to offline cache your application 'shell' and populate the content using JS. This means you can get pixels on the screen without the network, even if the content eventually comes from the network - a great performance win. In browsers without SW, we gracefully degrade to server-side rendering (e.g iOS). |
| 3 | +A modern web application architecture leveraging [Service Worker](http://www.html5rocks.com/en/tutorials/service-worker/introduction/) to offline cache your application 'shell' and populate the content using JS. This means you can get pixels on the screen without the network, even if the content eventually comes from the network - a great performance win. In browsers without SW, we gracefully degrade to server-side rendering (e.g iOS). [Demo](https://app-shell.appspot.com/). |
4 | 4 |
|
5 | 5 | ## Goals
|
6 | 6 |
|
@@ -51,49 +51,21 @@ Alternatively, you can just run `npm monitor`. The application shell should now
|
51 | 51 |
|
52 | 52 | We've deployed the project to Node.js on [Google Cloud](https://cloud.google.com/nodejs/). To do the same, follow the steps in their Node.js deployment [getting started](https://cloud.google.com/nodejs/getting-started/hello-world) guide and after running `npm install` run `gcloud preview app deploy app.yaml --promote`. If everything works correctly, you should have the project deployed to your custom AppSpot endpoint.
|
53 | 53 |
|
54 |
| -## Why? |
| 54 | +## Notes |
55 | 55 |
|
56 |
| -[Service Workers](http://www.html5rocks.com/en/tutorials/service-worker/introduction/) are fantastic for offline caching but they also offer significant performance wins in the form of instant loading for repeat visits. This is possible with just a few changes to our overall application’s UI architecture. |
57 |
| - |
58 |
| -We can offline cache our application shell without the network being present and populate the content for it using JavaScript. This allows us to get meaningful pixels from our UI on the screen without the network, even if our content eventually comes from there. Think of it as displaying regions of the screen where toolbars and cards will eventually be populated very quickly and then loading in the rest of the content progressively. |
59 |
| - |
60 |
| -## How? |
61 |
| - |
62 |
| -During the first load experience, our goal is to get meaningful content to the user’s screen as quickly as possible. To achieve this: |
63 |
| - |
64 |
| -* **Server** will send down HTML content the client can render and will use far-future HTTP cache expiration headers to account for browsers without Service Worker support. It will serve filenames using hashes to enable ‘versioning’ and easy updates for later on in the application lifecycle. |
65 |
| -* **Page(s)** will include inline CSS styles in the document <head> to provide a fast first paint of the application shell. Each page will asynchronously load in the JavaScript necessary for the current view. As CSS cannot be asynchronously loaded in natively, this can be emulated using JavaScript (or something like the Filament Group’s [loadCSS](https://github.com/filamentgroup/loadCSS) project). |
66 |
| -* **Service Worker** will store a cached entry of the application shell so that on repeat visits, the shell can be loaded entirely from the SW cache unless an update is available on the network. |
67 |
| - |
68 |
| -In the background, we will register our Service Worker following this lifecycle: |
69 |
| - |
70 |
| -| Event | Action | |
71 |
| -|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------| |
72 |
| -| Install | Cache application shell and other SPA resources | |
73 |
| -| Activate | Clear out old caches | |
74 |
| -| Fetch | Serve up single page web app for urls.progressively cache future content - unless it’s no-cache header. Cache in a ‘content-cache’ to separate from the web app | |
75 |
| - |
76 |
| -#### File revision |
| 56 | +## Tips for your application shell |
77 | 57 |
|
78 |
| -One question that arises is how exactly should file revisions/updates be handled. This is application specific and the options are: |
| 58 | +In a [Progressive webapp](https://infrequently.org/2015/06/progressive-apps-escaping-tabs-without-losing-our-soul/), everything necessary to load the the simplest "shell" of your UI consists of HTML, CSS and JavaScript. Keep this shell as lean as possible. Some of the will come from your application’s index file (inline DOM, styles) and the rest may be loaded from external scripts and stylesheets. Together, they are all you need to display a simple, static app. It’s important to keep the shell of your webapp learn to ensure that some inline static structural content can be displayed as soon as the webapp is opened, regardless of the network being available or not. |
79 | 59 |
|
80 |
| -* Network first and cache old version otherwise |
81 |
| -* Network only and fail if offline |
82 |
| -* Cache the old version and update later |
| 60 | +A static webapp that always displays the same content may not be what your users expect - it may well be quite dynamic. This means the app may need to fetch data specific to the user’s current needs so this data can come from the network / a server-side API but we logically separate this work for our app from the application shell. When it comes to offline support, structuring your app so that there’s a clear distinction between the page shell and the dynamic or state-specific resources will come in very handy. |
83 | 61 |
|
84 |
| -#### Tooling |
| 62 | +## Gotchas |
85 | 63 |
|
86 |
| -* Use [sw-toolbox](https://github.com/GoogleChrome/sw-toolbox) for runtime caching, with varying strategies depending on the resource: |
87 |
| - * [cacheFirst](https://github.com/GoogleChrome/sw-toolbox#toolboxcachefirst) for images, along with a dedicated named cache that has a custom expiration policy of N maxEntries. |
88 |
| - * [networkFirst](https://github.com/GoogleChrome/sw-toolbox#toolboxnetworkfirst) or fastest for API requests, depending on the desired freshness. fastests might end up being fine for everything, but if there’s a specific API feed that gets updated very frequently, we can go with networkFirst for that. |
89 |
| -* Use [sw-precache](https://github.com/GoogleChrome/sw-precache) for caching the application shell |
90 |
| -should handle the concerns around file revisions, the install/activate questions, and the fetch scenario for the app shell. |
| 64 | +There are no hard and fast rules with this architecture, but there are a few gotchas you should be aware of. |
91 | 65 |
|
92 |
| -## Tips for your application shell |
| 66 | +* Requests for application content may be delayed by various processes such loading of the app shell, loading of JavaScript or fetch requests. Jake Archibald hacked around this by initiating the data request in his Wikipedia offline web app as he [served the shell](https://github.com/jakearchibald/offline-wikipedia/blob/master/public/js/sw/index.js#L59). |
93 | 67 |
|
94 |
| -In a [Progressive webapp](https://infrequently.org/2015/06/progressive-apps-escaping-tabs-without-losing-our-soul/), everything necessary to load the the simplest "shell" of your UI consists of HTML, CSS and JavaScript. Keep this shell as lean as possible. Some of the will come from your application’s index file (inline DOM, styles) and the rest may be loaded from external scripts and stylesheets. Together, they are all you need to display a simple, static app. It’s important to keep the shell of your webapp learn to ensure that some inline static structural content can be displayed as soon as the webapp is opened, regardless of the network being available or not. |
95 |
| - |
96 |
| -A static webapp that always displays the same content may not be what your users expect - it may well be quite dynamic. This means the app may need to fetch data specific to the user’s current needs so this data can come from the network / a server-side API but we logically separate this work for our app from the application shell. When it comes to offline support, structuring your app so that there’s a clear distinction between the page shell and the dynamic or state-specific resources will come in very handy. |
| 68 | +* In the application shell architecture downloading and adding content can interfere with progressive rendering. This can be an issue for larger JavaScript bundles or longer pieces of content on slow connections. It might even cause performance issues when reading content from the disk. Where possible *include* meaningful page content with the initial download rather than making a separate request for it. In the Wikipedia application, Jake was loading third party content and had to work around this, which is why he used the [Streams API](https://github.com/jakearchibald/offline-wikipedia/blob/master/public/js/page/views/article.js#L86). We strongly recommend reducing the number of requests made for your page content if at all possible. |
97 | 69 |
|
98 | 70 | ## License
|
99 | 71 |
|
|
0 commit comments