preface
For PWA, after being interrogated by the interviewer for many times 😭, I have a strong interest in him. I was too busy with the interview some time ago, so I was delayed to 😂. However, when it comes to learning new technologies, we always need to study them with a heart of awe. The rise of a technology always has its meaning and inevitably represents a trend. Having said all that, what exactly is a PWA?
What is the PWA
PWA (Progressive Web Apps) uses modern Web apis as well as traditional Progressive enhancement strategies to create cross-platform Web applications. These apps are ubiquitous and feature rich, giving them the same user experience benefits as native apps
The explanation on MDN is always very official, literally speaking, we can know that it is a gradual Web application, so what is gradual? In fact, it means that if the browser does not support this technology, then the original application will not have an impact, for the browser that supports this technology, it will add its features on the basis of the original, so that users get a better experience. The technology is currently integrated in Vue, React scaffolding, and once you have a Web app project, your PWA journey is already underway.
Why is it so popular? Which brings us to its three main features:
- Can be added to the desktop
- Offline access
- The background to inform
For a Web site, how to retain users has to be considered, and for Web applications, a crude way to be remembered by users is to bookmark, but in terms of user experience level, this is not comparable to native applications. For a large project, the cost of developing a native app is enormous.
So how can we make a Web application as desktop as a native App adding directly accessible and having the transition effect of opening a website became an urgent development need, and PWA was born.
Detailed implementation of the three features
One important point to note in THE PWA is that it only supports HTTPS and localhost, which means your application must have this condition in order to be accessed.
Desktop add
The core of this feature is a file called manifest.json, which can be installed to the desktop once our application introduces this configuration.
The manifest configuration
{
"name": "HackerWeb".// Application name
"short_name": "HackerWeb".// Short name for desktop display
"start_url": ".".Url / / entry
"display": "standalone".// The presentation mode of the application. This mode is generally the best for the experience
"background_color": "#fff".// The theme color of the application will generally change the background color of your upper menu bar
"description": "A simply readable Hacker News app.".// Application description
"icons": [{// Display the app icon in different environments
"src": "images/touch/homescreen48.png"."sizes": "144x144"."type": "image/png"}}]Copy the code
For details about the configuration, see Web App Manifest
Once configured, we just need to use the link tag to introduce
<link rel="manifest" href="manifest.json">
Copy the code
So your application already has the ability to be installed to the desktop, is not very simple 😏.
Offline access
The realization of this description function, the author began to prepare to enlarge the trick 🐤, one of its core concepts can be described in a picture:
In fact, the implementation of this technology needs to use our ServiceWorker and this Cache Storage to cooperate with the implementation.
The implementation idea is that ServiceWorker can intercept all requests, and can operate the Cache Storage for access operations, if the user is disconnected, we can choose to read the required data from the Cache, so that we can achieve offline caching 🤒.
ServiceWorker,
- Service workers allow Web applications to be used in poorly networked or offline environments
- Service workers can greatly improve the user experience of web apps
- Service worker is an independent worker thread, independent of the current page process, is a special Web worker
- Web workers are temporary, and the results of each action cannot be persisted. If the same complex operation is performed next time, it will take time to do it again
- Once installed, it exists forever unless it is manually unregistered
- It can wake up when it’s in use and automatically sleep when it’s not in use
- Programmable interception of proxy requests and returns, cache files, cached files can be accessed by web processes (including network offline state)
- Offline content developers can control
- Must work in HTTPS environment
- Asynchronous implementation, mostly implemented internally through promises
What is webWoker, this article will not repeat, detailed concept can refer to ruan Yifeng teacher’s blog, Web Worker use tutorial
Registered ServiceWorker
To use it, we usually sign up the first time a user visits the site. In order not to affect the normal page parsing and page resource download, we will choose to register ServiceWorker when the onLoad event is triggered. The registration of ServiceWorker is very simple, just need to call an Api:
window.onload = function() {
if (navigator.serviceWorker) {
navigator.serviceWorker
.register("./sw.js")
.then(registration= > {
console.log(registration);
})
.catch(err= > {
console.log(err); }); }};Copy the code
First, we will determine whether the browser supports ServiceWorker. If it supports ServiceWorker, we will register it. If not, we will skip it without affecting the page. The registration method returns a Promise object, which we can obtain in the THEN method. This object contains information about the successful registration. If the registration fails, we can catch it in the catch method.
Life cycle of serviceWorker
After registering our sw.js(filename customization) file, we can explore its three core lifecycle functions in the sw.js file.
- Install – will be in
service worker
Triggered when the registration is successful. It is used to cache resources - Activate – will be in
service worker
When activated, it is used to delete old resources - Fetch – intercepts all requests for a page. When a request is intercepted, it is triggered (core). It is used to manipulate cache or read network resources
Install phase
At this stage, we mainly store some pages, resources, etc., which need to be cached offline, so that we can continue to access the website without the network.
self.addEventListener("install".async e => {
cacheData(); // Call the cache method
await self.skipWaiting(); // Skip the wait
// e.waitUtil(self.skipWaiting()); // Another way to skip waiting
});
Copy the code
First I’ll call the appropriate cache resource method, and then self.skipWating is used to reactivate the install lifecycle function if your sw.js file is changed, but it doesn’t immediately trigger the Activite cycle. It will wait for the last sw.js to be destroyed before activating the next one. At this time, our newly registered sw.js will not be activated, so in order to make the newly registered sw.js take effect immediately, we can add this sentence to skip the wait.
Why are there two ways to write this place? SkipWating returns a Promise, which is asynchronous. We need to wait for it to complete before we move on to the next one. We can use async await to do this. You can also use a built-in utility method, waitUtil, to do this.
Let’s parse the cacheData method in our code:
// Cache method
const CHACH_NAME = "cache_v2";
async function cacheData() {
const cache = await caches.open(CHACH_NAME); // Open a database
const cacheList = [
"/"."/index.html"."/images/logo.png"."/manifest.json"."/index.css"."/setting.js"
]; // The list that needs to be cached
await cache.addAll(cacheList); // Cache it
}
Copy the code
In fact, we need to use the cache storage. In fact, it is a bit similar to a database, generally want to use a database, we need to open a database, each database has a name, meet these conditions, we can store data to the cache storage.
cache storage
- The Caches API operates like a database:
caches.open(cacheName).then(function(cache) {})
: turns on the cache and returns a promise matching the Cache object cacheName, similar to connecting to a databasecaches.keys()
Return a promise object containing all the cached keys (database name)caches.delete(key)
Delete the corresponding cache (database) based on key
- Common cache object methods (single data operation)
cache.put(req, res)
Use the request as a key and store the corresponding responsecache.add(url)
Make a request based on the URL and store the responsecache.addAll(urls)
Grab an array of urls and store the resultscache.match(req)
: Obtain the response corresponding to req
We need to make a list of the resources we need to cache, that is, the code in the cache ist, call the cache storage addAll method can be cached in the cache storage 😀.
Activate stage
In this phase, we will generally do nothing more than to delete the old resources or cache storage.
Since serviceWoker does not take effect immediately after it is installed and activated in the user’s browser, we will call an API during the activate phase to enable it to take effect on the first visit. The code is as follows:
const CHACH_NAME = "cache_v2";// The current database name is defined globally
self.addEventListener("activate".async e => {
/** find all database names, clean up the old version of the database */
const keys = await caches.keys();
keys.forEach(key= > {
// Delete the database name if it is not the name currently defined
if (key !== CHACH_NAME) {
caches.delete(key);
}
});
/** is used to gain control of the page immediately, ensuring that the first time the user opens the browser it takes effect immediately */
await self.clients.claim();
});
Copy the code
Since self.clients.claim() also returns a Promise object, we also need to wait for its execution to complete.
The fetch phase
Can say at this stage is to compare core lifecycle function, because the front two are mainly used for some initialization operation, and fetch it truly offline caching the hub, it intercepts all page request, because of this feature, we can in the case of no Internet users need to request the resource from the cache read out returned to the user.
In general, we will have a variety of strategies for processing user requests, the author will talk about two commonly used:
The network is preferred
As the name implies, it is to first go to the network request, if the request can not be obtained, then go to the cache to read, the specific code is as follows:
self.addEventListener("fetch".async e => {
const req = e.request;// Get the request header
await e.respondWith(networkFirst(req));// Return the requested resource to the browser
});
// Network priority
async function networkFirst(req) {
/** Use try. Catch to catch an exception */
try {
const res = await fetch(req);
return res;
} catch (error) {
const cache = await caches.open(CHACH_NAME); // Open a database
return await cache.match(req);// Read the cache}}Copy the code
The Fetch is used to make a request to the corresponding network address. If the resource is not requested, an exception will be thrown, which can be caught by the try catch, and then cached in the catch.
The cache is preferred
Read cache data first, if no network request.
// Cache is preferred
async function cachekFirst(req) {
const cache = await caches.open(CHACH_NAME); // Open a database
let res = await cache.match(req);// Read the cache
if (res) {
return res;
} else {
res = await fetch(req);
returnres; }}Copy the code
The specific code meaning will not be repeated, can see this step should be no problem for you 😜.
Once we have the resource, we just need to call the e.espondwith method to render the return value back to the browser. At this point, we have taken a big step. The last step is how to notify the system.
Notification
This processing part cannot be placed in the sw.js file because we need to use the Notification function in window.
// Obtain notification permission first
if (Notification.permission == "default") {
Notification.requestPermission();
}
if(! navigator.onLine) {new Notification("Tip", { body: "You're disconnected. You're accessing cached content." });
}
Copy the code
With such system-level apis, the natural first step is to obtain user permissions before proceeding to the next step. I’ve only written a feature here that lets users know they’re offline.
The last
The author’s list of files:
- images
- logo.png
- index.css
- index.html
- manifest.json
- sw.js
- setting.js
- server.js
In the index.html file, you just need to introduce manifest.json and setting.js with the link tag. The contents of setting.js are as follows:
window.onload = function() {
if (this.navigator.serviceWorker) {
this.navigator.serviceWorker
.register("./sw.js")
.then(registration= > {
console.log(registration);
})
.catch(err= > {
console.log(err); }); }};/** * Determine whether the user is connected to the Internet, and give notification prompt */
// Obtain notification permission first
if (Notification.permission == "default") {
Notification.requestPermission();
}
if(! navigator.onLine) {new Notification("Tip", { body: "You're disconnected. You're accessing cached content." });
}
Copy the code
In great detail also wrote more than 3k words, hope to be able to help you, but also welcome you to express the incorrect place to correct 🧐.