Progressive Web App tutorial – learn to build a PWA from scratch

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hi there my name is marcus helberg and in this video i'm going to show you how easy it is to build a progressive web application to do that i'm going to first build a very simple news reader application from scratch and once we have it up and running i'm going to take that and turn it into a progressive web application step by step just so you can see hands-on what the different steps are that are involved in building a progressive web application so let's start we're going to create a new folder here on my desktop call it news let's go into that folder and then here open up our code editor of choice i'm going to use vs code and of course you can use whatever editor you're most comfortable with in here we're going to scaffold out the project so let's create all the files that we need we're going to need an index file let's just populate the file with a just a blank template give it a title of news here we can just scaffold the basic layout of the application so we're going to have a header section and here we're going to have a h1 tag just for the title of our application and then we'll have a main section where we'll actually populate all the news articles in addition to that we'll have a style sheet so we'll just have a link here and just styles css and will also include a javascript file for all the application logic let's call it app.js then let's go ahead and create these files styleset css here i just have a ready-made template for the styles because we're not going to focus on the actual styling then we'll have the app.js file we can leave it empty for now and go ahead and serve this folder just so we can see that we are on the right track serve this close the console here and hop on over to chrome okay so you can see that we have the application is up and running here in my chrome dev tools i am on the application tab this application tab will be your best friend when you are developing and debugging progressive web applications so in here we can see all the different parts of a progressive web app like we can see the manifest file if we had one we can inspect service worker says they're running we can go in and clear all the data so it's easy for us to just reset everything and start from scratch and we can also just jump in and debug things as we're working on this okay so the first thing that we're gonna do is just build the plain non-progressive web application for the news itself i will use a free news rest api called newsapi.org and we're just going to use it to fetch some articles from different sources and display them let's copy this url here and go into our app.js file so the first thing i'm going to do is add a event listener for the load event so i don't want to block the initial load of the page with this rest call i just want to have the initial html display as fast as possible and once it's done then we can start actually fetching data so in here we're going to call a method called update news and create it here below like so here i'm using async functions and if you're not familiar with them i'll add a link in the notes below but basically they just make it easier for us to deal with promises in a much more readable fashion so be sure to read up on them if you're not familiar i'm going to use them throughout the example here so for the update news function the first thing that we're going to do is get the result or the response rather from fetching this url of course my api key is different from this so let's extract that into a variable and i'll define it up here just log in to the site and get my key real quick there we go like so all right so once we have the result we want to actually get the json that's in in that result so what we're going to do is again await and then result.json if we go back into the documentation here for articles we can see that the response looks like this so we get an object it has a array of articles and then each article has a field for things like author title description image and so on so let's go in here and what i'm going to do is a little bit of a hack but i don't want to use any any uh frameworks or any other tools just plain vanilla javascript here so uh what we'll do is just first of all get a hold of the main section where we want to put these so we're going to do a document query selector for the main section and here when we're updating the news what we're going to do is just say main.interhtml equals and then we're going to go through the json and the articles in there and map them to through a function that creates an article and then just join those articles with a new line to make them a little bit more legible in our in our html let's create the create article function here takes in the article json and spits out the actual markup so here i'm just returning a template string that has the the stuff that we need out of the article so we have the url we have the title we have an image and the actual description for it let's save this go back to our browser here and refresh and we can see that with that in place we're now able to actually see some articles in our app here um one thing that we still want to do in order to kind of complete the functionality of the application is add a little selector here where we can select which source we want to get our news from the api here has another endpoint that we can call for that just sources so let's go ahead and do that so we'll both update the news update sources and create a new async function for this sources and here we're going to do pretty much exactly the same thing that we did for the news so we're going to get the result from fetching the sources then we're gonna get the json from that result and and if we go in and look at the actual response here you can see that again we get an object and it has an array of sources so what we want to do is just loop through those sources and populate our selector now um of course we need a selector for that so let's just create a new select we don't need a name for it for this particular stuff what we do want is to give it an id let's call it source selector and here in our app again let's just get a handle to it selector equal query selector for selector like that so what we want to do here is just get the source selector that inner html equal to json.map and then we're going to just map each of those sources actually json.sources.net so we go through the sources array we map those to options and we can just again join them with new lines just for easier reading if we need to go in and inspect the actual markup later on okay so let's go into our browser refresh here see if that works now that did not work what did i do wrong source selector oh a little typo there something like cutter like that save both of those back in the browser refresh and we can see that we have the different sources here there's a couple of problems here first of all you can see that these are not in sync now so we're the selectors first item is abc news for australia but we're displaying techcrunch so let's go and change that behavior first of all uh let's change the default source for our news and then we'll make sure that those two stay in the sync so if we select another source we want to fetch the news from that source so let's define our default source i'm going to call this default source the washington post um post like that and then instead of just updating news always to fetch from text source uh techcrunch what i'm going to define is a a parameter source and we can default that to default source and then just change this hard-coded source here to our parameter like so all right and what i'm going to do then is just wait for the update sources to complete so we need to turn this into a async function for that to work and once we've actually fetched the sources then we can set the source selector's value to equal our default source again let's go into the browser refresh okay so now we can see that this actually matches so we have the news from the washington post and we see that washington post is selected the final little thing that we need to do here is have a listener for the select change and then actually fetch the newspaper on that so let's do so or selector that event listener for the change event and what we want to do here is very simply just call update news with event.target.value just get the value out of the select and call update news with that let's refresh here try it out select this yep so we can see that we can now select uh different sources for our news and get them displayed so we have the basic functionality of our application there's still nothing progressive about this web application but at least we have a very good understanding of how it works where it fetches its data from and so on okay so uh let's start looking at turning this application into a progressive web application now if we go in and look at the application tab here in chrome we can see that the first kind of sub tab here is manifest and the web manifest is really just a json file that collects all the information about your application into one convenient json file so this is basically the same information that you have in all those different meta tags uh in your browser or i'm sorry in your application header today so instead of typing this out myself i'm going to use a web app manifest generator that i found and we can just define the application name news we can give it a short name so if we had a really long name that wouldn't fit underneath the icon when this gets installed we can give a shorter name that would be used in place of that we can define the colors that we want our application to use we can define how it should be displayed in our case we want it to be standalone so kind of hide the browser chrome and everything and then i just wanted to start from whatever url the person was on when they browse to this application final thing we need is an icon and i can just drag my icon file here you need to upload at least a 512 by 512 image for this to work we generate the zip file here open it and what we'll just do here is take these two drag them into our application folder and then go back into our editor and see what we have so uh there are two things we have the manifest file here so you can see we have this json file with the name and short name and the theme colors and urls here and then we have this image folder which contains all these resized images so that we need using the manifest file is really easy the only thing we need to do is just add a link to it so the only thing we need to do is add a link to it and if we go back into our browser refresh this we can see that now the application tab here picks up our manifest we can see all of those parameters that we define we can see the icons and everything now if we click on add to home screen here it's going to complain that we don't have a matching service worker so in addition to having this manifest file you also need to have a service worker uh installed for the application in order for it to actually get picked up as a progressive web application by devices and for them to offer to install this so let's go ahead and create a service worker now service worker is just basically a normal javascript file it's kind of like a web worker in that it doesn't run in the same kind of thread as your main application rather it's just separate but it is just a plain javascript file where you that you program in javascript the way we uh include this is by first of all listen making sure that we have support for it so we want to make sure that we have uh sir this worker in our navigator object now if we do what we want to do is first of all do a little try catch here so we want to first of all try and see if we can do navigator dot service worker dot register and pass in our service worker file here we want to be careful about with the path of this file so in order for a service worker to handle the traffic it needs to be in the root of your application so it can only handle things that are in the same folder or child folders of where the actual javascript file is so make sure that you have it in the root of your application once that's done we can just uh do a quick log say service worker registered and of course if this fails we'll just log out registration failed go into our application again refresh and we can see that our service worker did actually get registered if we go into the service worker tab here we can see that the service worker file is registered right here we can click in and see the contents of it obviously there's nothing in there right now but that's a good start so let's go into our service worker file and take a look at it the service worker is completely event driven that means it can't just run stuff by itself because it feels like running a whole bunch of computation rather it can only get triggered by events so there are a couple of events that we want to listen for uh in this demo the first one is the install event so we'll just add an event listener for the install event and here we can just log out install then we'll do the same for the fetch event the fetch rent is basically when the service worker intercepts any network request going up from from your application to the net so we'll just do a self. ad event listener fetch and then we'll just log out fetch so we can see what's going on so we save that we go back into our application and we refresh now what you see here is that we have if we click on the active service worker we see it's still the empty one and we have this new service worker here waiting to activate so by default when you've registered a service worker it will continue to serve all the requests for that specific domain so in order for the next or the new version to actually take effect you either need to close all the tabs or you can kind of skip that by pressing skip waiting here now every time you make any change to your service worker the browser will detect that as a new service worker and especially during development time this will get pretty cumbersome so i recommend that you check this update on reload which will make sure that the new changes will always get included when you're running the application so if we refresh this now you can see that we got the newest changes applied here okay so we have our service worker file but it's not really doing anything yet what we want the service worker to do is first of all cache all the static assets that we have in our in our application so that we can serve them from the cache and get a really fast startup time for application and also make sure that we can at least display something when a user is offline so let's define a array of static assets set of cassettes and here we're just gonna define the different assets that we have so those are the exact same ones that we created in the beginning so we'll have the root of our application we have the styles.css file and we have the app dot js file i'm using relative paths here just because i want to run this from my github pages and if i'm not in the root of the domain having this relative path will make that work so with those defined we can actually start using our service worker for something interesting now here in the install event this will get called when a new service worker is discovered and it gets installed so what we want to do is use the cache api to take all of our static assets and save them for later the way we do that is first of all get a handle of the cache we do that by awaiting caches.open and then we have a name for our caches so let's call this new static for a way to work of course this needs to be an async function like that once we have the cache here we can just say cache dot add all and pass in our static assets save this and go to our browser now if we refresh here you can see that the cache tab here has a new kind of child node and we can see that it did actually cache all of these files that we told it to do now if we go to our service worker tab again and simulate an offline situation and refresh of course this doesn't work still so we did cache all of those assets we can see that they are in our cache but despite that we can't really do anything with them unless we instruct it to so the service worker is a really low level api it doesn't really do anything unless you tell it that it needs to do that so let's tell it what it needs to do first of all um here when we're intercepting fetch events these are the events sent from our application to the network uh let's first of all hold the request out of the event object here like so and what we can do in a service worker is we can define how we want to respond to a given fetch event so we can call event.respond with and then we can define how we want to respond with it so for our static assets here we want to um respond with the cast assets first and if we don't have anything in the cache we can go ahead and try and fetch them from the network so let's call this a cache first strategy so we'll just pass in our request and then create our function for handling that so we'll async function called cache first and it will take in this request now here we first want to check if we have a cached response so and we do that by calling caches.match with the request so the request itself is the key in the cache now this will return either undefined if there is nothing in the cache or with the cached response so we can then go ahead and return either the cash response or if there's none then we can call fetch with that request so that way we fall back on the network let's save that go back into our browser refresh check our service worker file just make sure everything got picked up so we can say we actually have all the new code here which is good we can check our cache storage we can see that we have everything in there so if we go offline now you can see that it doesn't give us the offline dinosaur what we get instead is basically the application shell so the the shell of the application is the static part of the ui the part of the ui that remains the same regardless of what content you're actually viewing so it's already a little bit better it's not really a progressive web application or it is but it's not really that helpful to our users instead what we wanted to do is actually save news as we're browsing through them so that if we go offline we can then come back and at least read the news that we've viewed previously so let's refresh that and go in so in order to do that we're gonna have a different caching strategy so for any other news we actually want to go to the network first and try to always get the latest news and then if we're unable to do that we'll fall back on the cached version for that same news source so uh let's first of all get the url out of the request so create a new url object with the request.url and then if the urls origin is equal to location.origin so basically for fetching from our own site then we'll use this cache first strategy that's for the static assets else we're gonna call event dot respond with and do a network network first approach again let's go and implement this here so we'll have a async function called network first and that again takes in request here we're going to use a different cache just because i want to keep the dynamic assets separate from the static assets later on it would make it easier for me to clear out some of the dynamic assets as they get old so let's call this and again we're going to wait caches.open and get a new cache called news dynamic we'll then do a try catch and what we're going to try to do is go to the network and fetch news so we're gonna await the result of fetching the request if that worked we're gonna first of all store the request into our cache so we can fall back on it later we do that by just calling cache.put so instead of cache.ad that we used earlier here we're going to use put this allows us to actually define the request using ad would go and re-fetch it which is unnecessary in this case so we're gonna put the request there as the key and then we'll do response.clone for the actual content so we're gonna clone the actual result here because it can only be read once so if we put the actual actual response in there we wouldn't be able to return it to the to the browser okay so that's kind of if everything goes well and if things don't go well we'll just go sorry we're gonna of course await this and await cash dot match with the request so we're gonna try to go to the network put it into the cache if that fails if we're offline we're gonna try to see if we have something in the cache and return that instead so again go to our browser refresh this we can see now that we have two caches we have the dynamic cache and the static cache we can see that we have all kinds of different we have the json files here from our rest apis we have images and so on um so we go to our service worker tab again refresh we can see that this now works so we we are we are offline but we're able to browse the news articles that we had seen before now if we go to another source it doesn't work because we haven't been there before but as long as we keep kind of browsing these uh different articles uh we're able to kind of uh see those when we are offline now it would be nice if we could have some sort of fallback content so if i go to usa today and i haven't been there uh it would show us at least some sort of meaningful content and explain what's going on so let's do that so here uh i have a couple of files on my desktop first of all a fallback json response so that's something i'm going to respond with if we don't get something from the network or the cache and then i have a fallback image as well first thing we're going to do is cache those so we have them available to us if we are offline and we're unable to return anything meaningful to the user so we're gonna do the fallback.json and also images fetch dog.jpg so what we need to do then is change the kind of catch behavior here so instead of just returning whatever we have in the cache which may be undefined we're gonna save that into a cached response object and then very similarly to what we did here in our cash first response we will either return the cash response or we'll go to the main caches and uh and match on fallback.json the fallback json here is just a json file that follows the same same format as the news as the news responses itself so save that let's go into our browser again refresh make sure that we have everything in here we can see that the new service worker changes did get picked up go back into our application tab here see that it works let's go offline so we should be able to view the washington post since we've been there and if we go to usa today we'll get our fallback content here so um with that we basically have a pretty simple but working progressive web application so with this simple uh service worker in place we're able to have a progressive web application but as you can see that even for this fairly limited amount of functionality we already have quite a bit of code also this is not a very robust production ready solution so there are already some things that you can probably notice that we're doing kind of wrong here one of the things is that this dynamic cache here just keeps on growing and growing so we don't have any way of just kind of purging out old content as it gets unuseful to us we also don't have a way of updating single assets in our in our cache so in a real life situation one of the nice things with progressive web applications is that if we only change say the app.js file that's the only file that the browser needs to re-download so especially when you're creating applications for places where you have low or very expensive low-speed or very expensive data connections having the ability to just update specific assets in your application instead of having to go and redownload all of them like we are doing now is very helpful so in order to solve kind of both of these problems and to give us a easier way to build production quality service workers google has this tool called workbox so let's rewrite the service worker that we have currently using workbox and see how much we can kind of get rid of boilerplate with that so we can go ahead and delete all of that code that we wrote and go into our command line here i'm going to turn this into an npm project real quick like so then going to install workbox take just a couple of seconds here and with that install let's first of all go and serve this again so we don't forget that and what we can do is import the workbox script just to import script and pass in the path for workbox here in the scripts we have two different ones we have a development version and a production version let's let's copy the path to the development version that gives us a little bit more help along the way when we're developing this go in and make sure that we don't need the full path here just the relative path will do like so then we'll initialize our service worker or workbox rather so new workbox sw and then we can just call precache on that and pass in these static assets the same ones that we had from before so if we go to our browser here let's go to application first of all clear everything so we're starting from a clean slate let's refresh this you can see that first of all we have a cache here called workbox precaching revisioned you can see that we are getting some debug messages from workbox so we can see that it's up and running if we go into our application here go offline uh we're able to refresh this and see that we're back to the state where we were showing the application shell so that's already a lot less code than we had before the second thing that we want to do is cache the calls to the news api for that we can build use the built-in router in workbox so we can just call workbox.router.registerrout and here we can pass in an express style route and in our case the route we're interested in this news api.org anything and when this route gets hits what we want to do is call network workbox.strategies.networkfirst so uh by just using one of the ready defined strategies network first here we don't need to re-implement the the logic for that now if we go back to our browser here refresh this um we can see that we now have this runtime caching cache here as well that has the json for both the articles and the sources so if we go and make ourselves go offline we can see that we are getting the articles here but the actual images are not showing up but that's okay because what i really want to do here is define a special route or basically a special way of handling images so i'm going to use the cache first strategy that it comes with but i'm going to define some cache expiration parameters so i want to keep a maximum of 20 entries and i want to keep them for 12 hours at most here i'm also defining the status codes that i want to cash so 0 to 200 basically 0 being those opaque responses so let's save that go back online let's refresh this we can see that we now have this images cache here and if we start going through these different sources you'll see that we're now starting to expire old entries from our cache so this is a really great way for us to easily keep the size of our cache to manageable size we're not going to start consuming a lot of space on our end users devices so with that we have a again a working progressive web application this time with a lot less code and with something that's going to be a lot easier for us to deal with workbox comes with a built-in command line tool and also ways for you to call it from build tools like gulp so that's it for the video i hope you learned a lot about progressive web apps and i'll leave some links to further reading in the show notes below thanks for watching
Info
Channel: vaadinofficial
Views: 184,945
Rating: 4.9646797 out of 5
Keywords: pwa, progressive web app, service worker, web app manifest, tutorial, example, hands on, javascript, web development, mobile, offline, offline first
Id: gcx-3qi7t7c
Channel Id: undefined
Length: 39min 21sec (2361 seconds)
Published: Thu Nov 09 2017
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.