Introduction to PWAs
PWA (Progressive Web Apps) does not refer to a specific technology, but a Web App that applies multiple technologies to improve the user experience, providing a user experience similar to Native App for the Web App.
Its core technologies include Web App Manifest, Web Push, Service Worker and Cache Api, etc. User experience is the core of PWA.
The main features of PWA are as follows:
- Reliable - even when the network is unstable or even disconnected, the can be loaded and displayed instantly
- User Experience - Responsive, with smooth transition animations and feedback on user actions
User Stickiness - Like Native App, it can be added to the desktop, can receive offline notifications, and has an immersive user experience
write in front
- article does not explain the technical details of PWA in detail . If you are interested in PWA technology, some materials have been prepared at the end of the article. You can refer to the study.
- The purpose of this survey is not to fully access PWA technology for the website, but to use its caching mechanism to improve website performance
The main technology used is Service Worker + Cache Api
Front-end multi-level cache model
When the browser wants to fetch remote data, we don't immediately leave (send a request). In the computer field, many performance problems are solved by increasing the cache, and the front end is no exception.
Like many backend services, frontend caching is multi-level.- Local read phase, in this phase we will not initiate any HTTP requests, only read data locally as a response
- In the HTTP request stage, we initiate an HTTP request, but the data is still read locally. So far, we may not have made a real request. This also means that during the cache check phase we have many opportunities to nip subsequent performance issues in the bud
In the real request stage, if unfortunately there is no valid data locally, the real request will be initiated at this time
The detailed flow chart of the front-end multi-level cache is as follows:Why do you need Service Workers with HTTP caching?
In addition to serving PWA (push and message), service workers can bring some additional benefits over http caching in terms of caching for normal web.
can be understood as, SW is the browser to open a layer of cache management interface to developers.
The advantages are as follows:
1. Override the default behavior.
For example, when the browser is refreshed by default, it will re-initiate the request for all resources, even if the cache is still valid, but using SW, you can rewrite this behavior and return to the cache directly.
2. Cache and update coexist.
To make a webpage available offline requires a long cache for the entire site, including the HTML. However, HTML uses a long cache and cannot be updated in time (the browser does not have an open interface to directly delete an HTML cache). With SW, you can use the cache part first, and then initiate a SW js request. For this request, we can implement changes, modify the HTML version, and re-cache a copy. Then the user can see the new version next time they open it.
3. Non-invasive.
No intrusive version control. The optimal version control is generally to record the file names (HASH) of all js css in HTML, and then initiate requests as needed. Each resource is long cached. In this process, the project structure needs to be changed, at least one more js or a section of js to control the version number, and the winter file name needs to be injected into the url when initiating a request. Using SW, you can integrate this part of non-business logic into sw js.
No intrusive request statistics. For example, cache ratio statistics, picture 404 statistics.
4. Additional cache.
The HTTP cache has limited space and is easily flushed. Although some browsers also have an elimination mechanism for SW storage, but with one more layer of cache, the probability of hitting is higher.
5. Offline processing.
When offline is detected, and a picture is not cached, special processing can be done to return the offline prompt. Or make a pure front-end 404/disconnected page. Chrome-like little dinosaur page.
6. Preload resources.
This is similar to the prefetch tag.
7. Preprocessing.
For example to check if html/JS is hijacked by the operator? After the js file is executed by the UI process, the malicious code cannot be deleted, but in SW, we can easily solve it as text. Of course, the probability of hijacking in an HTTPS environment is extremely low.
Source: https://www.cnblogs.com/kenkofox/p/8732428.html
Service Worker
Introduction
The original intention of Service Worker is to optimize the user experience and bring silky smooth offline applications. But it can also be used as a site cache. It itself is similar to a web proxy between the browser and the server, which can intercept requests and manipulate the response content.
Service Worker adds persistent offline caching capability on the basis of Web Worker. Through its own life cycle feature, it can ensure that complex work is only processed once, and persistently cache the processing results until the inherent processing logic of Service Worker is modified.
The features are summarized as follows:
- A special worker thread, independent of the main thread of the current web page, has its own execution context
- Once installed, it exists forever unless unregistered
- The browser will automatically wake up when it is used, and automatically sleep when not in use
- It can intercept and proxy requests and process returns, and can operate local caches, such as CacheStorage, IndexedDB, etc.
- Offline content developer controllable
- Can accept offline messages pushed by the server
- Asynchronous implementation, the asynchrony of the internal interface is basically realized through Promise
- Cannot directly manipulate the DOM
Must be in HTTPS environment to work
user
Many teams also enable this tool to implement Service Worker caching, for example:
- Taobao Home
- NetEase News wap Article Page
- Baidu's Lavas
- fullstory
... ...
compatibility
As shown in the figure below, except for IE and Opera Mini, which are not supported, most modern browsers have no problems, and the compatibility is more than 96%.
safety
Service Worker is a worker thread that is independent of the browser main thread. It is completely isolated from the current browser main thread and has its own independent execution context. Since the Service Worker thread is a worker thread independent of the main thread, any operations in the Service Worker will not affect the main thread.
Therefore, in the case that the browser does not support Service Worker, Service Worker hangs, Service Worker fails, etc., the main website will not be affected, so it is 100% safe from the perspective of website failure.
The possible problem lies in the accuracy of data, which involves technologies such as caching strategy and elimination algorithm, and is also the focus of configuring Service Worker.
scope
Service Worker registration has unexpected scope pollution problems
SPA has only one index.html entry in the engineering structure. The content of the site is rendered in the front-end after asynchronously requesting data, and the page switching in the application is controlled by the front-end routing.
This index.html is usually deployed to https://somehost , and the SPA's Service Worker only needs to be registered in index.html once. Therefore, sw.js is generally placed directly in the root directory of the site to ensure accessibility, that is to say, the scope of Service Worker is usually /, so that Service Worker can control index.html, thereby controlling the cache of the entire SPA.
code show as below:
var sp = window.location.protocol + '//' + window.location.host + '/';
if ('serviceWorker' in navigator) {
// 为了防止作用域污染,将安装前注销所有已生效的 Service Worker
navigator.serviceWorker.getRegistrations().then(regs => {
for (let reg of regs) {
reg.unregister();
}
navigator.serviceWorker
.register(sp + 'service-worker.js', {
scope: sp,
})
.then(reg => {
console.log('set scope: ', sp, 'service worker instance: ', reg)
});
});
}
renew
When executing the navigator.serviceWorker.register() method to register the Service Worker, the browser can detect the update of sw.js through its own diff algorithm. There are two ways:
- Update of Service Worker file URL
- Updates to Service Worker File Contents
In the actual project, when the Web App is newly launched, usually when registering the Service Worker, the Service Worker is updated by modifying the URL of the Service Worker file. This part of the work can be achieved through the webpack plugin.
caching strategy
precache
Static resources are deterministic, so you can actively obtain the list of resources that need to be cached, and actively initiate static resource requests and cache them during the service worker installation phase, so that once the new service worker is activated, the cache can be used directly. This is a process of resource prefetching, so the caching scheme of static resources is also called pre-caching scheme. For more details about pre-cache, please refer to Pre-cache scheme
dynamic cache
In the Service Worker environment, you can send network requests to obtain resources through the Fetch API, or obtain cached resources through local caches such as the Cache API and IndexedDB, or even generate a Response object directly in the Service Worker. These are the sources of resource responses. . The role of the resource request response strategy is to solve the problem of where the response resources come from. For more request response policies, please refer to here
some advices
- HTML, if you want to make the page accessible offline, use NetworkFirst, if you don't need offline access, use NetworkOnly, other strategies are not recommended for HTML
- CSS and JS, the situation is more complicated, because the CSS and JS of the general site are all on the CDN, and SW has no way to judge whether the resources requested from the CDN are correct (HTTP 200). If the failed result is cached, the problem will be bigger. . This I recommend using the Stale-While-Revalidate strategy, which not only ensures the page speed, and even if it fails, the user will refresh it and update it
- If your CSS and JS are under the same domain as the site, and the Hash version number is in the file name, you can use the Cache First strategy directly
- The picture suggests to use Cache First, and set a certain invalidation event, the request will not change again
- All interface class caches recommend the Stale-While-Revalidate strategy
- Cache only and Cache first must not be used for any resources not under the same domain.
For more information on caching strategies, you can read the following articles:
PWA Workbox Cache Policy Analysis
Service Worker Development
The life cycle
Regarding the service worker life cycle, it mainly involves the update of the service worker itself and the corresponding resources cached at what stage. For more information click here
Cache API
Comparison of offline storage solutions
The front-end mainstream offline storage solutions are compared as follows:
The Cache API is tailor-made for the storage of resource requests and responses. It adopts the data model storage format of key-value pairs, with the request object as the key and the response object as the value, which exactly corresponds to the request and response when a network resource request is initiated. a corresponding relationship. So the Cache API is suitable for local storage of request responses.
IndexedDB is a non-relational (NoSQL) database. Its storage objects are mainly data, such as numbers, strings, Plain Objects, Array, etc., as well as a small number of special objects such as Date, RegExp, Map, Set, etc., for Request , Response These cannot be stored directly by IndexedDB.
As you can see, the Cache API and IndexedDB are functionally complementary. When designing a local resource caching scheme, the Cache API is usually used. However, in some complex scenarios, the Cache API has limitations in the form of one-to-one correspondence between requests and responses. Therefore, it is necessary to combine IndexedDB, which is more functionally flexible. , access some key data information through IndexedDB, assist Cache API for resource management.
compatibility
Summarize
Through the above comparison, we can use IndexedDB and CacheStorage to provide the underlying service for the offline storage of Service Worker. According to the experience of the community, their respective applicable scenarios are:
- Use CacheStorage for URL-addressable (such as scripts, styles, images, HTML, etc.) resources
- Other resources use IndexedDB
Workbox
Introduction
In the page thread, although the underlying API can be used directly to handle the registration, update and communication of the Service Worker, in more complex application scenarios (for example, different windows in the page register different Service Workers), we often have to deal with Various situations gradually lead to a complex and chaotic abyss, and when the operating results are inconsistent with the expected results, we are often overwhelmed and do not know how to troubleshoot. It is for these reasons that the Google Chrome team launched a PWA solution called Workbox, which includes core libraries and build tools, so we can use Workbox to achieve rapid development of Service Workers.
webpack plugin
The official workbox-webpack-plugin plugin provides us with further development cost savings (version v6.4.2)
Why do you need this webpack plugin?
- Hash the pre-cache and dynamically update the hash during development
- More convenient interface to dynamically cache configuration method, automatically generate and update sw
access code
const { InjectManifest } = require('workbox-webpack-plugin');
// 注入模式
new InjectManifest({
swSrc: path.resolve(__dirname, 'src/service-worker.js'), // 已有 SW 路径
swDest: 'service-worker.js', // 目标文件名(打包后)
maximumFileSizeToCacheInBytes: 1024000 * 4, // 只缓存 4M 以下的文件
include: [/.*.(png|jpg|jpeg|svg|ico|webp)$/, 'beautify.js'], // 仅包含图片和beautify.js
}),
service-worker.js complete code
// 基础配置
import { setCacheNameDetails, skipWaiting, clientsClaim } from 'workbox-core';
// 缓存相关
import { precacheAndRoute } from 'workbox-precaching/precacheAndRoute';
import { registerRoute, setDefaultHandler } from 'workbox-routing';
import {
NetworkFirst,
StaleWhileRevalidate,
CacheFirst,
NetworkOnly,
} from 'workbox-strategies';
// 插件
import { CacheableResponsePlugin } from 'workbox-cacheable-response/CacheableResponsePlugin';
import { ExpirationPlugin } from 'workbox-expiration/ExpirationPlugin';
// 内置方案
import { pageCache, offlineFallback } from 'workbox-recipes';
// 自定义插件去掉请求参数 t
async function cacheKeyWillBeUsed({ request }) {
const url = new URL(request.url);
// Remove only paramToBeIgnored and keep other URL parameters.
url.searchParams.delete('t');
// Return the full remaining href.
return url.href;
}
setCacheNameDetails({
prefix: 'sw-tools',
suffix: 'v1',
precache: 'precache',
runtime: 'runtime-cache',
});
skipWaiting();
clientsClaim();
/*
通常当用户访问 / 时,对应的访问的页面 HTML 文件是 /index.html,默认情况下,precache 路由机制会在任何 URL 的结尾的 / 后加上 index.html,这就以为着你预缓存的任何 index.html 都可以通过 /index.html 或者 / 访问到。当然,你也可以通过 directoryIndex 参数禁用掉这个默认行为
*/
precacheAndRoute(self.__WB_MANIFEST, {
ignoreUrlParametersMatching: [/.*/],
directoryIndex: null,
});
// 离线页面缓存
offlineFallback();
// URL navigation 缓存
pageCache();
// html 的缓存
// HTML,如果你想让页面离线可以访问,使用 NetworkFirst,如果不需要离线访问,使用 NetworkOnly,其他策略均不建议对 HTML 使用。
registerRoute(new RegExp(/.*\.html/), new NetworkFirst());
// 静态资源的缓存
//CSS 和 JS,情况比较复杂,因为一般站点的 CSS,JS 都在 CDN 上,SW 并没有办法判断从 CDN 上请求下来的资源是否正确(HTTP 200),如果缓存了失败的结果,问题就大了。这种我建议使用 Stale-While-Revalidate 策略,既保证了页面速度,即便失败,用户刷新一下就更新了。如果你的 CSS,JS 与站点在同一个域下,并且文件名中带了 Hash 版本号,那可以直接使用 Cache First 策略。
const staticMatchCallback = ({ request }) =>
// CSS
request.destination === 'style' ||
// JavaScript
request.destination === 'script' ||
// Web Workers
request.destination === 'worker';
registerRoute(
staticMatchCallback,
new StaleWhileRevalidate({
cacheName: 'static-resources',
plugins: [
new CacheableResponsePlugin({
statuses: [0, 200],
}),
new ExpirationPlugin({
maxEntries: 500,
maxAgeSeconds: 30 * 24 * 60 * 60,
purgeOnQuotaError: true,
}),
],
}),
);
// 图片的缓存
// 图片建议使用 Cache First,并设置一定的失效事件,请求一次就不会再变动了。
registerRoute(
({ request }) => request.destination === 'image',
new CacheFirst({
cacheName: 'images',
plugins: [
new CacheableResponsePlugin({
statuses: [0, 200],
}),
new ExpirationPlugin({
maxEntries: 200,
maxAgeSeconds: 30 * 24 * 60 * 60,
purgeOnQuotaError: true,
}),
],
}),
);
// 事件流接口的缓存
registerRoute(
/^http(s)?:\/\/((dev\.)|(test\.)|(testing\.))?xxxx.net\/api\/v\d+\/(.*)?\/session_events.*/,
new StaleWhileRevalidate({
cacheName: 'session_events_cache',
plugins: [
{ cacheKeyWillBeUsed },
new CacheableResponsePlugin({
statuses: [0, 200],
}),
new ExpirationPlugin({
maxEntries: 2000,
maxAgeSeconds: 7 * 24 * 60 * 60,
purgeOnQuotaError: true,
}),
],
}),
);
// play/init 接口
registerRoute(
/^http(s)?:\/\/((dev\.)|(test\.)|(testing\.))?xxxxx.net\/api\/v\d+\/(.*)?\/play\/init.*/,
new StaleWhileRevalidate({
cacheName: 'play_init_cache',
plugins: [
{ cacheKeyWillBeUsed },
new CacheableResponsePlugin({
statuses: [0, 200],
}),
new ExpirationPlugin({
maxEntries: 10000,
maxAgeSeconds: 7 * 24 * 60 * 60,
purgeOnQuotaError: true,
}),
],
}),
);
sample graph
figure 1
figure 2
Relevant information
- Front-end performance optimization - cache
- [MDN] IndexedDB Browser Storage Limit and Cleanup Standard
- NetEase Cloud Classroom Service Worker Application and
- workBox official
- using Workbox
- Workbox cache common example
- workbox routing request
- Taobao front-end Workbox application
- Magical Workbox 3.0
- 's PWA upgrade practice
- Baidu Web Ecological Team "PWA Application Practice"
- PWA in simple
- workbox-webpack-plugin plugin related
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。