Jeff is a member of Google's Web Developer Relations team, where he builds libraries that take the work out of using service workers.View the profile
About the talk
Building a Progressive Web App (PWA) doesn't mean building a single page app! This session will deep-dive into alternative architectures for content-focused PWAs, and help you make the right decision for your specific use case. You'll also get insights on how the Workbox project provides developers with an easy path towards using the web platform features that provide the foundation for these architectures.
So hello everybody. Hey, my name is Jeff poznick. And I'm a member of Google developer relations team. So I'm here to talk about an important but potentially misunderstood topic. The architecture that you use for your web app and specifically how your architectural decisions come into play when you're building a progressive web app. So what do I mean by web architecture? Well one way to think about it is ask yourself the following questions. What are user visits a page of my sight what HTML is loaded?
And then what's loaded when they visit another pitch? The answers to those questions are not as straightforward. And once I start thinking about Progressive web apps they can get even more complicated. So my goal during the session is to walk you through one possible architecture that I found effective. Throughout the talk. I'll label the decisions that I made as being my Approach for building Progressive web app. You're free to use my Approach when building your own TWA. But at the same time they're always other valid Alternatives as well. My Hope Is
that spicy how all the pieces fit together one Spire you and that you will feel empowered to go out and customize everything to suit your own particular needs So what are you doing Building C company? This talk is a stack overflow pwi. I personally spent a lot of time reading and also contributing to stack Overflow and easy to browse through frequently asked questions for a given topic. It's built on top of the public stack exchange API and you can try it out live and view The Source Code by using the Euros on
that slide. It's a before, you know, the specifics how I built that web app. Let's define some terms and explain pieces of underlying technology. 1st and B talking about what I like to call multi-page apps or MPS. Do you think of npas as a fancy name for the traditional architecture that's been used really since the beginning of the web? Each time a user navigates to new RL the browser progressively renders HTML specific to that page. There's no attempts to
The next. You've heard me and certainly countless others use the phrase Progressive web app or pwa. But what do I mean by that exactly? So you can think of a pwa as a web app that provides a first-class user experience and that truly are in some place on a user's home screen. The acronym fire standing for fast integrated reliable and engaging sums up all the attributes to think about when you're building a pwa. The purposes of this talk. However, we're going to focus on a subset of those attributes fast and reliable. A
fast means a lot of things in different contexts. We're going to focus on the speed benefits of eluding as little as possible from the network. The raw speed isn't enough in order to feel like a pwa your web app should be reliable. Mystery resilience enough to always load something even if it's just a customizer page regardless of the state of the network. And finally, we're going to rephrase the pwa definition slightly and look at it what it means to be reliably fast. That's not good enough
to be fast and reliable only when you're on a low latency Network. Being reliable fast means your web app speed is consistent regardless of the underlying that were conditions. Cpwa has introduced a high bar for Speed and resilience. Fortunately the web platform offer some building blocks to make that type of performance reality. I'm talking of course about service workers and the cache storage API. So a service worker sits in between your web application and the network acting as
a proxy that could intercept incoming requests. And what that service worker does is completely up to you. And it's basically stration the service worker takes incoming requests and froze amounts of network. Avengers returns to never responds to page as is It was trivial service worker doesn't actually add any value versus the default number Behavior. But once we add the cache storage API into the mix the power of service workers shine through. So we can build a service worker that listens for incoming requests like before. Passing some on to
extension for taking a code that lives in an e s module. So keeping those Concepts and terminology mines, let's dive until I actually built my stack overflow pwi. Remastered by covering a back-end server and explain how that fits into the overall architecture. So is looking for combination of nighttime Dynamic Falcons along the static hosting and my personal approach was used to Firebase platform. Firebase Cloud functions will automatically spin up a nude based environment when there's an incoming requests and integrates with the popular Express h-2b framework, which is already familiar with
It also offers out-of-the-box hosting for all of my sight static assets. And let's take a look at how that server handles incoming requests. So when browser makes a navigation request against our server and goes through the following flu the server routes the request based on the URL and logic to complete to create a complete HTML documents. Music combination of data from the stock exchange API as well as partial HTML fragments that the server stores locally.
pattern to match against. I can reference this mapping directly from the service code. when there's a match for a given Express pattern the appropriate Handler with template in logic specific to the matching rabbit is given a chance to respond. And what is that templating logic look like? Well, I want with an approach that piece together. Partially. It's no fragments in sequence one after another. This model lends itself really well to streaming response back to the browser.
The service sends back some initial HTML boilerplate immediately and the browser's able to reminder that partial cage right away. As a server pieces together the rest of the data sources and streams them to the browser as well until the documents is complete. 303 what I mean, let's take a look at the express code from one of our routes. Are using the responses right method and referencing locally-stored partial templates were able to start the response stream immediately without
It's going back to her route Handler. The next portion of our page uses data from the stock exchange API. Getting that data means that our server needs to make an egg a network request. We can't render anything else cuz we got a response back and process it. But at least our users and staring at a blank screen while they wait. And once we received the response from the stack exchange API, we call a custom template in function to translate the data from the API into its corresponding HTML.
So basic approach that produce static HTML work fine for me. Alright, so here's an example of how I'm template in the dynamic HTML portion of a web apps index. That's what our routes the template in logic is stored in an es module that can be imported into both the server and the service worker. And just a reminder when your whenever you're taking user-provided input and converting into HTML. It's crucial that you take care to properly Escape potentially
each link set to the stack exchange API Euro that we need in order to display the corresponding question. Keep our minds will Revisited later on? Okay, so jumping back to a route Handler. Once templating is complete we stream the final portion of a Page's HTML to the browser and we end the Stream. This is a key to the browser that the progressive rendering is complete. Okay, so that's a brief tour of our server setup. Users who visit our web app for the first time but always got a response from the server, but
when to visit visitor returns to our web app, our service worker will get a chance to start responding and what's time in there? Instagram she looks somewhat familiar many of the same pieces. We've previously talked about our here in slightly different arrangement. Let's walk through the request for for the service worker taking all of its logic into account. Their service worker handles and incoming navigation request for giving you oral. And just like a server did it has to use a combination of routing and templating logic to
figure out how to response. Approaches the same as before, but with different low-level Primitives like thatch and the cache storage API. We use those data sources to construct or HTML response, which the service worker passes back to a web page. So rather than starting from scratch with those low-level Primitives, we're giving a builder service worker on top of a high-level set of high-level libraries old work-box. So I'm a member of the workbox engineering team and this is not exactly unbiased but I think using workbox provides a solid foundation for any service workers cashing
routing and response generation logic. Brett the next time slides. We'll see how work box is put to use. First let's cover service worker routing. Justice Weather Service, I'd code or service worker needs to know how to match up an incoming request for the appropriate response logic. My Approach was the translate each express route into a corresponding regular expression making use of a helpful Library called regex program. Once I translations performs, we can take advantage
of work boxes built-in support for regular expression routing. So after importing the module that has our regular Expressions, we register each one with work boxes router. Inside each route. We're able to provide custom template in logic used to generate a response. Templating in the service worker is a bit more involved than it was in her back end server of work box helps with a lot of the heavy lifting. The first we need to make sure that are partial
HTML templates are locally available in the cache storage API. And are always kept up-to-date whenever we deploy changes to a web app. Couch maintenance can be are prone when done by hand. So we trying to work box to handle pre cashing as part of a build process. Retail workbox which euros and free cash using a configuration file pointing to the directory that contains all of our local assets along with the set of patterns to match. This file is automatically read by the workbox CLI, which is run each time. We rebuild our
sites as part of my build process. So workbox takes a snapshot of each files contents and automatically injects that list of euros and revisions into a final service worker file. Workbox now has everything it needs to make sure our pre cash flows are always available and always kept up to date. Prefer to use more complex build process where each box has both of webpack as well as a generic node module interface in addition to his command line. What's the next we want
a service workers stream that precache partial HMO back to the web app immediately without any delays. This is a really crucial parts of being reliably fast. We always get something meaningful on the screen right away. Fortunately using the streams API within our service worker makes a possible. No, you might have heard about the streams API before my colleague. Jake Archibald has been singing the Praises for years now made this bold prediction back in 2016. And the
streams API is just as awesome today as it was two years ago Over The crucial difference. One week room supported the streams back. Then the streams API is much more widely supported now. I so know careful observers my note that asterisk. So here are the caveats for today. Firefox's worth of streams is behind a flag, and it's not yet enable by 50 falls, but you can try it out if you go in and manually make that change. And it's also a known bug that lead to issues with edges current streams implementation. But the overall story is positive and
with appropriate fallback code. There's nothing stopping you from using streams in your service worker today. Well, there might be one thing stopping you and that's wrapping your head around how the streams API actually works. Expenses a very powerful set of Primitives and developers who are comfortable using it can create really complex data flows. But understanding that the full implications of code similar to what's on the screen right now might not be for everyone. Rather than parse through this logic. Let's talk about
my approach to service worker streaming. So I'm using a brand new high-level rapper provided by workbox. I can pass it in a mixer stream sources what from caches and runtime data that might come from the network. Workbox will take care of coordinating the individual sources and stitching them together into a single streaming response. Moreover workbox automatically detects whether the streams API supported and when it's not it creates an equivalence Zone on streaming response.
This means that you don't have to worry about writing fullbacks as streams inch closer to 100% browser support. Alright, let's turn our attention to how our service worker deals with run time data from the stack exchange API. So we're making used to work boxes built-in support for a Stihl while rebelde cash and strategy. Long with expiration to ensure their storage doesn't grow unbounded. Let's take a look at how those Concepts translate into code. here we set up two strategies and work box and handle the different
sources. They'll make up the final streaming response. If you function calls and some configuration workbox, lets us do what would otherwise take hundreds or even thousands of lines of handwritten code. For the first strategy will be used to read data that's been pretty cashed like a partial HTML templates. The other strategy implements that still while we validate cashing logic along with the least recently used cache expiration once you reach 50 entries. Now that we have the
strategy someplace all that's left is to tell work box how to use them to construct a complete streaming response. A pass in an array of sources as functions and each of those functions will be executed immediately. Workbox takes result from each source and streams it to the web app in sequence only delaying if the next function in ear a hasn't completed yet. So the first two sources are Precast partial templates that read directly from the cache storage API and so they'll always be available immediately.
This ensures that our service worker implementation will be reliably fast and responding to requests just like our server side code was Arnex, quartz function fetches data from the stack exchange API and processes response is the HTML that are webapp expects. The still well-regarded strategy means that we have a previously cash response for this API call will be able to stream it to the page immediately while updating the cash entry in the backgrounds for the next time that it's requested.
Finally we stream in a cached copy of our footer and close our final HTML tags to complete the response. So now we run through the service worker code certain bits. Hopefully look familiar. The partially Sumo and sampling logic used by a service worker Is identical Twitter cervicide Handler uses? This code sharing and sure is that users get a consistent experience whether they're visiting or at web for the first time or if they're returning to a page
response. Because we end up using the same initial bit of cash HTML for each page. We end up with generic tags in our documents head. Better coordination with between are templating and our clients I'd code we can update the windows title using page specific metadata. So is part of the templin code include a script tag containing the properly Escape string? Then once our page has loaded we read in that string and we update the document title. If there are other pieces of page specific medidata, you want to update in your own web app. You
Remember the special data attribute. We talked about containing the URL for the API request needed to display a question. So we could cross-reference those data attributes against the list of cash URLs and create an array of all the question links that don't match. This gives us all the data. We need to create handlers that responds to the browser going online or offline. Tsuna brother interest in offline States. We Loop through the list of uncashed links and dim out the ones that won't work. So keep in mind that this
is just a visual hints to the user about what they should expect from those pages. We're not actually disabling. The links are preventing the user from navigating. I want to browser comes back online. We restore those links to their original paths today. I have is now going through a tour of my approach to building a multi-page pwa. There's a lot of factors that you'll have to consider when coming up with your own approach and you may end up making different choices and I did and that flexibility is one of the great things about building for the
web. But there are few common pitfalls that you may encounter when making your own architectural decisions, and I wanted to call them out in advance to hopefully save you some pan. So first, I really recommend against storing complete HTML documents in your cash. for one thing it's a waste of space your web app uses the same basic educational structure for each of its Pages you end up storing copies of the same mark up again and again, More importantly though. Let's see you to play
that's not always possible. Whatever architectural decision you end up making you need to have some strategy for running the equivalent routing and templin code in your server and your server work service worker. So what happens when you ignore those pitfalls? Mom, also it's all sorts of failures are possible. But the worst case scenario is that a user returns your site and navigate to rounds? Eventually reversing a cached page for the very stale layouts.
So it's jarring when you see it happen during a presentation and it's just as distracting for your web apps repeat visitors. So do everything you can to avoid this. Alternatively, they might come across the URL that's handled by your server. But is no longer handled by your service worker. Experience full of zombie layouts and routing dead-ends is the opposite of shipping a reliable pwa. But you're not in this alone. The following tips to help you avoid those pitfalls. Try to use templating and routing libraries that have
portions of your page use more complicated template and logic as long as you can stream in the initial parts of your HTML as quickly as possible. Best performance. You should precache all of your sites critical static resources. the sausage stuffer run time Cashing routes to handle Dynamic content API requests using workbox means that you can build on top of well-tested production-ready strategies instead of implementing it all from scratch. And related to that you should only block on the network when it's not possible to stream a response from the cash.
Displaying a cached API response immediately can often lead to a better user experience than waiting for that fresh data. If you follow the general guidelines that have covers today you're on your way to building a first-class pwa experience. Experience of fast learning as little as possible from the network and the case my stackoverflow pwa almost all the bikes were looting or for actual contents with almost no overhead. I'm building an experience that's reliable, but it's playing something meaningful even when you're offline.
Are pwa uses visual cues to reinforce that we're providing reliable experience. An experience that's reliable fast by streaming in our initial issue immediately. followed by Page specific content regardless of a network conditions that about wraps it up for me were interested in your feedback on this presentation. Please go to our website or use the Android or iOS apps. Let us know what you think. And if you want to learn more about the topics covered these links will help. So thanks everybody for watching. Thank you.
Buy this talk
Access to all the recordings of the event
Buy this video
With ConferenceCast.tv, you get access to our library of the world's best conference talks.