How often, when requesting a page, we see the message “No internet connection”. However, it has long been possible to catch events in the absence of the Internet and control the content that the user sees. Alexey Chernyshev and Maxim Chagin launched a offline version of the Russian Newspaper website - the official publication of the Russian Government with 1 million visitors per day - and shared their experience on RHS ++ 2017. Decoding their report under the cut.

Offline version is already available for any site.
Let's imagine - we have a pizza delivery service. The client enters, but he does not have internet. He waits for 20 seconds and sees the same thing: “No internet connection”. I don’t think it will stop him from ordering if we have a really tasty pizza.
But are you sure that in order to visit the site, choose a pizza, fill it with ingredients, send an order form, do we really need internet? No, of course! All this can be done offline, and when the connection appears, send the order already to the server for processing.
')

If the Internet has not appeared, you can send a notification: “Oh! Contact the phone manager or stay hungry! ”The option is certainly not perfect, but better than nothing.
This can be absolutely any case. If you have an online store, you can inform the user about the sale. In the case of informational content, you can give interesting material to read.
Service Worker - the heart of offline applications
All offline work is based on the use of the Service Worker. He has a lot of properties, but we'll talk only about the basic basic parts, which will help to understand what is happening with offline.
So, the
Service Worker is an event-driven worker who receives a JavaScript file, executes it, and allows you to:
✓ Monitor the pages in the context of which this
Service Worker was invoked;
✓ Intercept various requests and modify them on the pages under control;
✓ Cache resources through a special cache API.
I would also like to note that it works in a stream parallel to the main one, and accordingly, the DOM, LocalStorage or the Window object is not available to it. But then, performing some actions, it does not block the main UI browser.
The principle of operation of the Service Worker
In order for the Service Worker to be able to earn money on the site, and all the magic could act, it must be registered, then it must go through the installation phase and the activation phase.

Register Service Worker
On the client, you need to call the code that passes the path to our Service Worker. If this file is available and it can be executed without errors, then the Service Worker is considered registered, and begins to listen and view those pages that are in its scope.

A little bit about the scope
- If the Service Worker is put in the root of the site, next to Favicon, then he will see all the pages that are inside the site.
- If you put it in a subdirectory, it will only act in the page that is inside this directory.
- You can programmatically limit its scope through the scope property. In the example, we have limited it to work only in the article subdirectory.
Install Service Worker
The first event that occurs is the installation event. By processing this event, we cache resources, which we will later use to render the client an offline version, via the API cache to the browser.

Service Worker Activation
After this, an activation event is triggered. Note that if the Service Worker is only registered for the first time and activated on the page, then the activation event immediately triggers and you can do something by handling this event.

When updating the Service Worker, the order of activation is different. If before this site already had some version of it, and we changed the file, the browser, which checks identity after about 24 hours, understands: “Yeah, I have a new version of the Service Worker on the site, let's update it!”
After that, the new Service Worker starts registration and installation. But, if the user has opened the pages with the old Service Worker, the new Service Worker goes into standby mode. Only after they are closed, the activation event is triggered, and it can continue to work, at this point you can tidy up the old Service Worker and remove unnecessary data.
We listen to events
After we have processed the activation, the Service Worker starts working in full force, namely, the following events are available to it:
- Message allows the client side to tell the Service Worker something to do, i.e. send a message via Post message.
- Fetch is one of the most important events in the offline method. Occurs when a resource is requested in the pages controlled by the Service Worker. By processing the Fetch event, we can send the desired response to the page.
- Push occurs when a push notification to a browser comes from a service.
- Sync occurs when we want to update some data for the Service Worker on the client in the background.
Approaches to building offline applications
Now that we know what a Service Worker is and that it can be used to manage the cache, let's highlight the main strategies for building an offline application.
1. Static.

We take in general everything that is on the page - HTML. CSS, JS, Fonts, SVG, IMG and throw in the cache. This is the easiest and fastest way to make your application available offline, but it has a significant drawback. It is suitable for rarely updated static content, if the information changes more often than the user visits the site, then perhaps it will simply lose its relevance and become useless to no one.
This option can be used to cache the conference schedule. We already have speakers, everything is compiled and compiled - thrown into the cache, and everyone is happy.
2. Offline first, or unkillable service.

This is a way to create an application where an internet connection is a kind of improvement, not a necessity. By default, we believe that we have no Internet. All server logic is transferred to the client.
As soon as the Internet appears, the client part is synchronized with the server part, if necessary, and the user continues to work, as if nothing had happened.
For example, we have a news publishing service, the editor can write and do anything there, without the Internet, and when the Internet appears, everything is synchronized with the server, and it will be possible to display content on the site.
3. SPA

This is a deployment of a mini-application with its structure, addressing, design. As we have said, in such a mini-application, we intercept the request from the user, and in case he does not have the Internet, we expose some content.
It can be anything you want - a game, an interactive, a poll. This option is not suitable for services due to the fact that we actually have a substitution of content, but for an information and entertainment resource, this is an ideal option.
At home, we use it will tell about it a little more.
The idea of creating an offline version of rg.ru
So, at the Moscow JS conference, colleagues from Lenta.ru told that they didn’t suggest playing tic-tac-toe if the user lost the Internet while reading an application. The idea itself seemed interesting to us, but one toy is not enough. We reasoned that the user sees all the same message and it is trite.

Another option: the user visits the site and, as long as he does not have the Internet, he is offered to read articles. This is already better - at least there is a chance that we have captured the user's attention, and after he has the Internet, he will continue to read further on the main site.
Interface description
The first thing that comes to mind when you try to imagine an offline application is the 2-page SPA: the main page with a list of news, and you can open any one for reading.
An important question is how to inform the user that he has opened an offline version so that he does not think that the site now looks like this. We tried a lot of different options, but we stopped at the simple, maximally noticeable dice at the top, which says that this is an offline version and there is no advertising here. In the upper right corner there is a red indicator that says “Offline”, as soon as the Internet appears, it turns green, indicating that a landing page is available.

Filtering content
It should be noted that not all the materials in a row are offline. This is due to the specifics of information content. Imagine, the application can be opened both in a week and a month after the last visit, and in June-July we will show the user something like “May 20 in Moscow will warm to the climatic norm”. On the other hand, advice on how to maintain health after 25 is always relevant.
How does the offline version of the site rg.ru
In order for this to work, you need to visit the site at least once. After entering the site, the Service Worker is initialized, adding all the necessary information to the cache.
Offline application architecture
The user enters the site and on the client side of the page the registration of the Service Worker is turned on, we give the user to the cache several files for the offline version, and after that we delete the unnecessary old ones.
Next, we begin to check the availability of the page itself through the Fetch event. Namely, by processing the Fetch event, we see that if the requested resource is available, then we simply give it away. If not, we leave to the exception and give our offline version via the API cache.

While the Service Worker checks and processes everything, we find out in parallel whether the user needs to update the data for the offline version.
The principle of the application
As already mentioned, the offline version is a SPA application built on the standard MVC architecture: there is a controller, which in our case receives only 2 addresses, the main page or the page of the article. We take the data from the cache and the piece of the template we need and give the user the ready-made HTML.

By the way, here we do not use any libraries like React or Angular. We only have the Mustache template engine and regular JavaScript - just so that all this does not drag the user into the browser.
Network availability
Now you need to decide how to know that the Internet has appeared. Events ONONLINE and ONOFFLINE, unfortunately, are not suitable, because they occur when the network cord is pulled out of the network card. And we went to the forehead, and it turned out to be effective.
After a certain time interval inside the application, statistics are sent to Google Analytics - if the statistics are sent, we understand that the Internet has appeared and inform the user about it.
Problems…
When we developed this version and launched it on production, we faced a number of problems that we want to share with you, so that you do not experience the same rake.
Data update
- Cap, we have a problem! It is necessary to update the cached data so that the user always sees the current version of the news.
- Maybe you have any ideas? You develop it!

The first thing that comes to mind, of course, is the setinterval inside the Service Worker — why not? We conducted a small experiment. Inside the Service, Worker wrote a function that, in a cycle, sent a simple get request to the server every 10 seconds, all data was logged. After that we go to the test page so that the Service Worker is initialized, close the tab and wait for the Service Worker to die.
It turned out the following:- In Google Chrome, on the desktop, the Service Worker dies, that is, it stops sending requests, in exactly 1 minute.
- FireFox sends requests indefinitely as long as the browser is open. The Service Worker continues to work.
- In the mobile Chrome, Service Worker also works endlessly, even if you remove the browser from applications, it still sends requests.
In principle, you can work with setinterval. But we did not use it:- Because it is not known how much battery it consumes.
- It is not known what will happen in the next version, suddenly Google will restrict sending requests to one minute, like on a desktop.
Therefore, we, as with the network accessibility check, decided to take a detour. If the user visits the page when there is internet:
- It records the time stamp in Local Storage.
- When the page is reloaded or re-visited, the timestamp is again removed and compared to the one that was recorded in Local Storage.

Thus, it calculates how much time has passed since the last visit to our site, and if it is more than 15 minutes, a request is sent to the Service Worker: “My friend, it’s time to delete the old data from the cache and write new data into it!”
The documentation says that Background Synchronization is being developed, that is, in the future, it will be possible to update the data for the Service Worker through the PeriodicSync method in the background. But for now we use proven time stamps.
Delays in receiving data
When we tested the offline application in the metro, a situation arose when the application was initialized, but the data did not arrive. What could happen? With the incomprehensible state of the Internet in the metro, the Service Worker did not understand whether there is Internet or not, and there was a delay.
It was decided very simply. We do not send the request through Fetch, but via PostMessage from our SPA application, ask the Service Worker: “Well, my friend, give us the data that you have in the cache!”
Two Service Workers in one scope
We already had one Service Worker with a push notification service, and now we have developed a second one. Just asking them different scope is not suitable, because both push-notifications and offline version should work throughout the site.

You cannot set the root Scope either, because the Service Worker installed later will replace the earlier version. It was also impossible to make one Service Worker file and import scripts with different services into it due to similar event handling.

Therefore it was necessary to rewrite everything trivially. More precisely, the logic remained, but we made one large PWA application to work in the Service Worker and already connected separate services to it.
At the moment, this is only Push notifications and an offline version, but perhaps in the future, such a modular approach will be useful to us for something else.
Banal advice - it is better to make a fully modular structure in the Service Worker in advance, so that you do not have to rewrite later.
Proxy traffic through the Service Worker
The moment came when we seemed to have done everything, everything was debugged and we run the Service Worker into our offline version for production.

But not everything went smoothly, on all pages of the site the offline version worked. But there were pages with a video player, when turned on, the video was given with a huge delay in minutes, because it also went through the Service Worker.
We realized that through the Service Worker we don’t need to skip anything but the page document itself, and added the appropriate filter. However, it still did not work, because the video was also given inside the Service Worker with the same title. I had to additionally exclude video from all requests.
Analytics
There is another problem - this is analytics. We do not have the Internet, requests are not sent, but we really want to know what is happening there.
The easiest option is to send data to GA, if the Internet appears in the process of reading the application. But what to do with users who have closed the application before the appearance of the network. Let's store the analytics on the client, and send it later. We decided that Index DB would be the best storage option.
This is the standard for storing large amounts of structured information on the client, which
- works asynchronously;
- supports transactions;
- able to work with indices;
- works directly directly from the Service Worker.
In our small application, only the first and last points are needed.

Now the scheme looks like this:
- The user walks the page;
- All pageview data is sent to the database;
- If the data is sent, we believe that the Internet is there, the database is cleared;
- If there is no Internet, the request is not sent, we are waiting for the next cycle.
Screwing this thing, we found about 10% of the missing audience.
What do we have at the moment?
When we first started, the data was, say, so-so - 2 thousand users per day. For our site, this is a small figure, which affected, among other things, the need to install the Service Worker, that is, a kind of user infection.
At the moment we have about 6 thousand users per day. This is about 180,000 per month.

We also measure how many users came to us from an offline to the main version of the site - that's about 3,000. It's easy to calculate that half have gone somewhere - we are dealing with this.
When we just launched the application, we counted on a mobile audience - subway, cottage, fishing, etc. But after the launch, it turned out that 30% are desktops. For us it was, frankly, unexpected! On desktops, we still see it as just a mobile version, but plans to fix it.
Unexpected use of the Service Worker. Blocking sites
Some time ago there was news about blocking the Russian media in Ukraine. In general, the topic of locks and we have quite relevant. As long as admins set up endless mirrors, you and I can inform the user about moving to another domain or give some information.
The same Service Worker will help us in this!
When a provider blocks a site by urla, in most cases it makes a simple 302 redirect. As a result, we just need to catch 302 server response status via Fetch.
As already mentioned, when we go through Fetch to exceptions, we vice versa catch request and check for response.status. Here response.status = 0 is not 302 or 301, namely 0. In the case of redirects, the Service Worker does not understand at all what happens on your server and gives the status 0.
To catch 5XX server errors, it is necessary to modify the previous version. All the same, but the status comes right, so we can safely catch and show some content.
This can be used if you have a server lying tight, while admins are trying to pick it up, the front heroically gives some information either from the cache or from a backup source.
A little bit about browser support
In IE, Edge and Safari Service Worker 'does not work :
- Ie you can immediately forget.
- Edge is under development with no specific dates.
- Safari says support is being considered for the next 5 years.
In fact, it is there, but turned off by default. It can be enabled through the developer’s functions, but for production and for users you will not launch it.

In mobile browsers, it's not that bad either. Everything works in the Android Browser version 5-6 and in Chrome for Android - that's about 55% of our audience.
Summary
✓ Offline is working now.
✓ As a bonus, you can catch errors, redirects, and in general any statuses.
✓ Expanded accessibility of the site - now you are not only online, but also offline.
PS: Working with offline is part of PWA (Progressive Web Applications), but we specifically bypassed this topic.

Gitkhab Alexey Chernyshev profiles -
https://github.com/nanomen and
Maxim Chagin -
https://github.com/maxchagin .
Answers to questions from listeners are often full of interesting information. We hid them under the spoiler, practically in its original form, i.e. with the maximum amount of details.
Questions and Answers- I have 2 questions:
1. First - I would like to once again understand how the scripts that Service Serviceer has downloaded are disabled?
2. The second question is - do I understand correctly that when I open some news page with popular articles, then, in theory, you can download them in advance so that the next time I click on it on the site, the request for the server did not go, and the saved page would be downloaded?
Maxim: I will first answer the second question. Theoretically, yes, it is possible. But the fact is that the resource is updated very quickly. You are not interested in what was yesterday? There is an option with push-notification. Users who are subscribed to the Push notification when it arrives, the Service Worker wakes up, and after that it can load the article that we “pushed” into the cache. In this case, it can open it even without the Internet.
In other cases, we just update some interesting articles. Our editorial board marks interesting articles with a special tag and it is sent to the user in advance in the cache.
Alexey: The first question. There is a registration, installation and activation. These 3 events occur only at the moment when either the Service Worker is installed on the site for the first time, or when we in the Service Worker file changed hands and reloaded it. This hash sum looks every day, is checked by the browser, it was updated or not.
Only at this moment the registration event is triggered again. Just at the time of registration, he checks whether it is necessary to update him further or not. And then installation and activation pops up.
The most difficult moment is activation. Imagine - there are 2 versions of Service Worker: old and new. The old one works. The user has open tabs with the old version. He opens a new page, and he already has a new Service Worker trying to break through.
For developer reasons, if we update the Service Worker to everyone now, then all these old pages will break. You never know what our Service Worker does? Therefore, the new Service Worker is installed; it can do something at the time of installation — write something to the cache. And after that it is blocked and starts to wait. He has an endless wait until the pages that use the old Service Worker are closed.
After we close them all or completely the browser, the new Service Worker asks for activation. At this moment we can do something (and we can not do it) - there is a certain toolkit in the Service Worker, and we can use it (or not use it). We can set the event and not activate the activation. There we are cleaning up what is not needed - the old cache. Then activation arises - that's all.
- Judging by your slides, you have nothing at all on Safari?
Yes, it all works only on Android. In Safari, this does not work in principle. If you open the cell phone, it will not work. We had a problem - the management only has iPhones, they come in and say: “Guys, what are you doing at all ?!”
- Hence the question. According to your own statistics, this is about 50% of the audience. Did you try to use AppCash as a fallback?
Maxim: No, not tried. In fact, we did this thing, so that it was not that we could play, but it was interesting. Therefore, there is no point in cramming something;
Alexey: What's the matter? It all started, as I said, with the notification. That is, it seems that we already have a Service Worker, and then we looked at the report on Lenta.ru — they did tic-tac-toe — why not? But let's not the game, but the news feed. And it somehow went.
That is, we didn’t have a task - and now we’ll also be hooked offline. It somehow happened by chance. Just what the most interesting thing is that actually writing this offline version is not very difficult. The most difficult to understand how these events are fulfilled. And just to give something from the cache is not at all difficult.
We wrote it down quickly, and then problems began to arise from this, about which I spoke, including with analytics.
Maxim: We still have a specific problem - it is monetization. That is, until we monetize offline, it’s rather useless to do something for the rest of the audience. There is no advertising.
- But what about the loyalty of users?
Those who do not know that the site is offline, they do not know. This is not what the need is, it is a bonus, appendage.
Alexey: I would even add that this is more likely for those who, in principle, visit the site, and not just go there from a search engine by accident. This is a bonus for regular visitors. I noticed that my wife was stuck and all the way to the subway was stuck with news. Unfortunately, you need to somehow find out about this. We didn’t hang any banners on this issue, we didn’t tell anyone that we were offline.
- What can be done to make the application run faster during the initial download? I'm trying now - it can hang for about 30 seconds. In principle, cool - there are 10 articles there, it’s enough to stick in the subway.
Maxim: If you do not have internet, then the browser tries to connect to it. At this time we can not show anything.
Aleksey: That's the funny trick of the Service Worker - inside it, when we process Fetch, this request goes to Promise, and it waits - either to go to resolve, or to reject. At this moment, hypothetically, maybe, something can be thrown out of it, but hardly. We either process it or this.
That was the problem - this is our sore subject, when, by foolishness in the SPA application itself, we requested data through Fetch offline. Since there is no Internet, why do you request data via Fetch? Accordingly, the system tries to obtain this data, but there is no Internet, and so there are delays.
The same story, if I press "Back" - it is a little more annoying. I went to the online version, then the Internet disappeared, I got offline, clicked "Back" - and he thinks for a long time trying to send a request, but it fails.
If the mobile and WI-FI are turned off in the settings, then in principle, then very quickly, then it must catch. There would be a method to understand that the Internet is not too good to give you the full version - you have to work on it.
- We also recently made an offline version, but we did not make a separate one. There is a very short question - do you really have 50% iOS?
45% iOS.
And about the technology itself - you, if I understood correctly, first try to get data over the network, then submit it from the cache. You can do the opposite and periodically update them.
And the second question - what do you do with media resources? An article is not just a text, it is also some kind of interactive kit, and what about your traffic, as a result?
Maxim: We specifically look forward to a mobile audience, so we do not load any media resources there at all. That is, there are no photos. Therefore, we have the following iteration - loading media there. We are going to optimize it for the desktop, display images, previews and load media content. I think we most likely will not get to photo reports and videos, but some small media will be there.
Alexey: So it happened, we love users. I just don't want to push everything into the browser. Hypothetically, you can really roll up the full article in the cache, no problem. But why?
Maxim: We have an advertising service that shoves banners 2-3 MB each. If we still do this, it will turn out badly.
Alexey: I will add about a cache. There is still a difference: there is a cache in which the browser caches some page, this is understandable. He caches it himself and we cannot manage it. But just the API cache - if we cache until we delete it ourselves, it will roll there. Therefore, I do not want to.
- Two questions. Firstly, you said that you suddenly had a third offline desktop and you are going to do something about it. But you figured out who these people are?
Maxim: Yes, these are people mostly from the regions. You can even see - we have a monitor, and the offline version is displayed there in real time, where we look at the regions. Falling attendance - hop! - some region has been cut off. Desktops are mostly from there. That's life. We here in Moscow do not know about it. It turns out people on the desktop have no internet.
- The second question is - you said that you can catch the redirect and at this moment in case of blocking something should be pushed through. It's not very clear. If you are already blocked, how can you tell where to move?
Maxim: This must be done in advance in the Service Worker. We all know in advance which one of us will be blocked. What is there to hide? – , – - - – , . .
, , , , ! !
. , , , Service Worker – . import script , — , CDN. Service Worker , , .
, : «, !» , – ! 5 , Safari .
, – . , , , 5 – .
: Service Worker JavaScript. . Fetch' . Service Worker , Fetch . 6 . – ! , , Service Worker, .
Come share experiences
Dear friends, for sure you also face interesting challenges and find elegant solutions. Do not underestimate them, it seems to you that everything is simple, because you have already done everything, and someone else does not know where to start.Choose the appropriate direction at the RIT ++ conference festival and send the application to the program committee. For those who doubt their lecturing abilities, we remind you that we will help you with this .