Application of Service Worker
Service worker essentially acts as a proxy server between Web applications, browsers and the network (when available). This API aims to create an effective offline experience. It will intercept network requests, take appropriate actions and update resources from the server according to whether the network can be used. It also provides an entry to push notifications and access the background synchronization API.
describe
In essence, Service Worker is also used for browser Cache resources, but it is not only a Cache, but also further optimized through worker. It is based on h5 web worker, so it will not hinder the execution of the current js thread. Its main working principles are: 1. Background thread, which is independent of the current web thread; 2. Network agent, which intercepts the request when the web page initiates a request, To return cached files. In short, a Service Worker is a worker thread running in the background. It will run for a long time and act as a service. It is very suitable for functions that do not require independent resource data or user interaction. The most common purpose is to intercept and process network requests. The following is a detailed description:
- Based on web worker (an independent thread independent of the JavaScript main thread, which performs operations that consume a lot of resources without blocking the main thread).
- Based on web worker, the ability of offline cache is added.
- Essentially, it acts as a proxy server between the Web application (server) and the browser (it can intercept the requests of the whole site and make corresponding actions - > actions specified by the developer).
- Create an effective offline experience (cache some infrequently updated content in the browser to improve the access experience).
- Event driven with a lifecycle.
- You can access cache and indexDB.
- Support push.
- Developers can control and manage the content and version of the cache.
There are other usage scenarios for service worker, and the standard of service worker can be used to do more to make the web platform close to native applications:
- Background data synchronization.
- Respond to resource requests from other sources.
- Centrally receive data updates with high computational cost, such as geographic location and gyroscope information, so that multiple pages can use the same set of data.
- Compile modules such as CoffeeScript, LESS and CJS/AMD on the client and manage dependencies (for development purposes).
- Background service hook.
- Custom templates are used for specific URL patterns. Performance enhancement, such as prefetching resources that users may need, such as the next few pictures in the album.
- You can cooperate with App Manifest and Service Worker to realize PWA installation, offline and other functions.
- Background synchronization: start a service worker to update the cache even if no user visits a specific site.
- In response to the push, start a service worker and send a message to the user to notify that new content is available.
- Respond to time or date.
- Access to geofencing (an application of LBS).
Example
Implement a simple service worker application example, which can also be used when the network is disconnected. The relevant code is shown in https://github.com/WindrunnerMax/webpack-simple-environment/tree/simple--service-worker , here is a simple example of using native service workers. Writing native service workers directly is cumbersome and complex, so you can use some libraries such as Workbox. There are some precautions before using service workers:
- The Service worker runs on the worker, which means that it cannot access the DOM.
- It is designed to be completely asynchronous, and synchronous APIs (such as XHR and localStorage) cannot be used in service worker.
- For security reasons, Service workers can only be hosted by HTTPS, and http can be used for localhost local debugging.
- In the user privacy mode of Firefox browser, Service Worker is not available.
- Its life cycle has nothing to do with the page (when the associated page is not closed, it can also exit, and when there is no associated page, it can also start).
First, use Node to start a basic web server. You can use the anywhere package. Of course, you can use other servers to access it after executing commands http://localhost:7890/ Just. In addition, it is recommended to restart the service after writing the relevant code. I have encountered the problem of unable to cache before, including disk cache and memory cache, which can only be solved by restarting the service. Also, the link to be opened is localhost. The browser automatically opened may not be localhost, so you need to pay attention. If you want to clean up the cache, you can click Clear site data in the Application project of the browser console to clean up all caches in the website. If you use server environments such as express or koa, you can also try to use Service Worker to cache data requests. You can also provide the path of data requests.
$ npm install -g anywhere $ anywhere 7890 # http://localhost:7890/
Write an index HTML files and SW JS file and import related resource files. The directory structure is as follows, which can be used for reference https://github.com/WindrunnerMax/webpack-simple-environment/tree/simple--service-worker Of course, you can run a static file server directly from the clone.
simple--service-worker ├── static │ ├── avatar.png │ └── cache.js ├── index.html └── sw.js
You can introduce relevant files into html, mainly to use the browser environment, and the focus is js.
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Service Worker</title> <style type="text/css"> .avatar{ width: 50px; height: 50px; border-radius: 50px; } </style> </head> <body> <img class="avatar" src="./static/avatar.png"> <script type="text/javascript"> navigator.serviceWorker .register("sw.js") .then(() => { console.info("login was successful"); }) .catch(() => { console.error("login has failed"); }); </script> <script src="./static/cache.js"></script> </body> </html>
The first step in using Service worker is to tell the browser that a Service worker script needs to be registered. Here, we directly write it to index HTML file. By default, the Service worker is only effective for the root directory. / if you want to change the effective range, you can add the second parameter {scope: "/xxx"} in the register, or you can directly specify the path / xxx / SW when registering js.
navigator.serviceWorker .register("sw.js") .then(() => { console.info("login was successful") }).catch(err => { console.error("login has failed") })
Once the registration is successful, the next step is the work of the service worker script. The following codes are written in the service worker script. After registration, the install event will be triggered. The service worker script needs to listen to this event. First, define the name of the cache, which is equivalent to identifying the key value of the cache object. The following urlsToCache array is the data to be cached. As long as the relevant path is given, even the data request can be cached, not just the resource file. Of course, it must be used under the request of Get, which is determined by the cache API. Then install, about event waitUntil can be understood as the function of new Promise, which is to wait for serviceWorker to run before continuing the following code. The actual parameter accepted can only be a promise. The explanation of MDN is that it takes some time before oninstall and onactivate are completed. The service worker standard provides a waitUntil method. When oninstall or onactivate is triggered, it is called to accept a promise. Before this promise is successfully resolve d, functional events will not be distributed to the service worker. Then, the cache is fetched from the caches_ The key of name identifies the cache, and then use cache Addall tells the cache the path in the array. When it is opened for the first time, the service worker will automatically request relevant data and cache it. The data requested by the service worker will display a small gear icon in the Network of the Chrome console, which is easy to identify.
const CACHE_NAME = "service-worker-demo"; const urlsToCache = ["/", "/static/avatar.png", "/static/cache.js"]; this.addEventListener("install", event => { event.waitUntil( caches.open(CACHE_NAME).then(cache => { console.log("[Service Worker]", urlsToCache); return cache.addAll(urlsToCache); }) ); });
Then comes the activated phase. If sw is loaded for the first time, it will directly enter the activated phase after installation. If sw is updated, the situation will be more complicated. The process is as follows: first, the old sw is A, the new sw version is B, B enters the install phase, and A is still in the working state, so B enters the waiting phase. Only after A is terminated, B can replace A normally. There are several ways for this terminated opportunity: 1. Close the browser for A period of time. 2. Manually clear the Service Worker. 3. Skip the waiting phase directly during sw installation. Then it enters the activated phase to activate sw work. The activated phase can do many meaningful things, such as updating the key and value stored in the Cache. In the following code, Cache that is not on the white list is implemented_ Name is cleaned up. You can implement A version here, that is, version control. Previous versions need to be cleaned up. In addition, I also checked the current relevant caches.
this.addEventListener("activate", event => { // Not on whitelist ` cache_ Just clean up const cacheWhitelist = ["service-worker-demo"]; event.waitUntil( caches.keys().then(cacheNames => { return Promise.all( cacheNames.map(cacheName => { if (cacheWhitelist.indexOf(cacheName) === -1) { return caches.delete(cacheName); } }) ); }) ); // Check the cache event.waitUntil( caches.open(CACHE_NAME).then(cache => cache.keys().then(res => console.log(res))) ); });
Then comes the stage of intercepting requests. This stage is a sw key stage, which is used to intercept all specified requests of the agent and perform corresponding operations. All cache parts are in this stage. First of all, we directly intercept all requests. The first judgment operation is to prevent all requests from being intercepted, so as to initiate requests in the worker. Of course, it can be used without judgment. Then, if the request matches the cache, it will get the data directly from the cache, otherwise it will use fetch to request a new request. In addition, if necessary, we do not need to match the event response, and we can directly cache all the initiated requests.
this.addEventListener("fetch", event => { const url = new URL(event.request.url); if (url.origin === location.origin && urlsToCache.indexOf(url.pathname) > -1) { event.respondWith( caches.match(event.request).then(resp => { if (resp) { console.log("fetch ", event.request.url, "There is a cache. Fetch from the cache"); return resp; } else { console.log("fetch ", event.request.url, "No cache, network access"); return fetch(event.request); // //If necessary, we don't need to match the event response. We can directly cache all the initiated requests // return fetch(event.request).then(response => { // return caches.open(CACHE_NAME).then(cache => { // cache.put(event.request, response.clone()); // return response; // }); // }); } }) ); } });
Console output when first opened:
cache.js loaded [Service Worker] (3) ['/', '/static/avatar.png', '/static/cache.js'] login was successful (3) [Request, Request, Request]
Console output opened for the second time and after:
fetch http://localhost:7811/static/avatar.png has a cache. Fetch from the cache fetch http://localhost:7811/static/cache.js has a cache. Get from the cache login was successful cache.js loaded
So far, we have completed a simple example. When we open the page for the second time, we can disconnect the Network connection of the browser, for example, close the file server or select Offline in the Network of the console. We can also see that the page is still loaded normally without Network services, In addition, the information (ServiceWorker) can also appear in the Size column of the relevant data of the Network, indicating that the resource is the cached data loaded from the ServiceWorker. Can be in https://github.com/WindrunnerMax/webpack-simple-environment/tree/simple--service-worker Run this example after clone comes down in.
<!-- index.html --> <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Service Worker</title> <style type="text/css"> .avatar{ width: 50px; height: 50px; border-radius: 50px; } </style> </head> <body> <img class="avatar" src="./static/avatar.png"> <script type="text/javascript"> navigator.serviceWorker .register("sw.js") .then(() => { console.info("login was successful"); }) .catch(() => { console.error("login has failed"); }); </script> <script src="./static/cache.js"></script> </body> </html>
// sw.js const CACHE_NAME = "service-worker-demo"; const urlsToCache = ["/", "/static/avatar.png", "/static/cache.js"]; this.addEventListener("install", event => { event.waitUntil( caches.open(CACHE_NAME).then(cache => { console.log("[Service Worker]", urlsToCache); return cache.addAll(urlsToCache); }) ); }); this.addEventListener("activate", event => { // Not on whitelist ` cache_ Just clean up const cacheWhitelist = ["service-worker-demo"]; event.waitUntil( caches.keys().then(cacheNames => { return Promise.all( cacheNames.map(cacheName => { if (cacheWhitelist.indexOf(cacheName) === -1) { return caches.delete(cacheName); } }) ); }) ); // Check the cache event.waitUntil( caches.open(CACHE_NAME).then(cache => cache.keys().then(res => console.log(res))) ); }); this.addEventListener("fetch", event => { const url = new URL(event.request.url); if (url.origin === location.origin && urlsToCache.indexOf(url.pathname) > -1) { event.respondWith( caches.match(event.request).then(resp => { if (resp) { console.log("fetch ", event.request.url, "There is a cache. Fetch from the cache"); return resp; } else { console.log("fetch ", event.request.url, "No cache, network access"); return fetch(event.request); // //If necessary, we don't need to match the event response. We can directly cache all the initiated requests // return fetch(event.request).then(response => { // return caches.open(CACHE_NAME).then(cache => { // cache.put(event.request, response.clone()); // return response; // }); // }); } }) ); } });
// cache.js console.log("cache.js loaded"); // avatar.png // [byte]png
One question per day
https://github.com/WindrunnerMax/EveryDay
reference resources
https://github.com/mdn/sw-test/ https://zhuanlan.zhihu.com/p/25459319 https://zhuanlan.zhihu.com/p/115243059 https://zhuanlan.zhihu.com/p/161204142 https://github.com/youngwind/service-worker-demo https://mp.weixin.qq.com/s/3Ep5pJULvP7WHJvVJNDV-g https://developer.mozilla.org/zh-CN/docs/Web/API/Cache https://developer.mozilla.org/zh-CN/docs/Web/API/Service_Worker_API https://www.bookstack.cn/read/webapi-tutorial/docs-service-worker.md