引言
最近next项目有使用pwa技术,使用起来也不复杂,目前浏览器的兼容性也比较良好
Service Worker是浏览器中独立于网页运行的脚本,而PWA(渐进式Web应用程序)是一种Web应用程序,其外观和感觉类似于原生应用程序。在讨论Service Worker与PWA之前,让我们先简要了解一下Web Worker。
Web Worker
1. 什么是 Web Worker?
Web Worker 是浏览器内置的线程,用于执行非阻塞事件循环的 JavaScript 代码。由于 JavaScript 是单线程语言,一次只能处理一个任务。复杂任务的出现可能导致主线程被阻塞,严重影响用户体验。Web Worker 的作用是允许主线程创建 worker 线程,使它们可以同时运行。Worker 线程主要负责处理复杂的计算任务,然后将结果返回给主线程。简而言之,worker 线程执行复杂计算,同时保持页面(主线程)的流畅性,不会造成阻塞。
2. 类型
Web Worker 有三种主要类型:
- Dedicated Workers【专用 Worker】由主线程实例化,只能与主线程通信。
- Shared Workers【共享 Worker】可以被同源的所有线程访问。
- Service Workers【服务 Worker】能够控制其关联的网页,拦截和修改导航、资源请求,并缓存资源,使您能够在某些情况下灵活控制应用程序的行为。
3. 限制
1. 同源限制
分配给 Worker 线程运行的脚本文件必须与主线程的脚本文件同源,通常都应该放在同一项目下。
2. DOM 限制
Web Workers 无法访问某些关键的 JavaScript 特性,包括:
1 DOM(因为这可能导致线程不安全)
2 window 对象
3 document 对象
4 parent 对象
3. 文件限制
出于安全考虑,worker 线程无法读取本地文件。它们加载的脚本必须来自网络,并且必须与主线程的脚本同源。
什是Service Worker?
Service Worker(服务工作线程)是一种在浏览器背后运行的脚本,用于提供强大的离线和缓存功能,以改善 Web 应用程序的性能和可靠性。它是渐进式网络应用程序(Progressive Web App,PWA)的关键组成部分,可以让 Web 应用程序更像本地应用程序,即使在离线状态下也能正常工作。Service Worker 是 Web 开发中的一个强大工具,它使开发人员能够更好地控制和管理 Web 页面的资源缓存、网络请求和响应,从而提供更快速、更稳定的用户体验。
Service Worker 的功能和优点
Service Worker 提供了许多重要功能和优点,其中包括:
1. 离线支持
Service Worker 可以缓存 Web 应用程序的资源,使其在断网或低网络质量环境下仍能够加载和运行。这意味着用户可以随时访问应用程序,无需依赖网络连接。
2. 更快的加载速度
通过将资源缓存在本地,Service Worker 可以显著提高 Web 页面的加载速度。它可以从缓存中获取资源,而无需每次都从服务器重新下载。
3. 支持后台同步
Service Worker 允许在后台执行任务,例如数据同步或推送通知。这使得应用程序可以在不干扰用户的情况下执行一些重要的操作。
4. 增强的安全性
Service Worker 受同源策略的限制,因此它可以提供更安全的资源缓存和请求处理。它还可以用于拦截和处理恶意请求。
5. 支持推送通知
Service Worker 具有推送通知功能,可以通过浏览器向用户发送实时通知,提高用户参与度和留存率。
使用
代码语言:javascript复制// 注册 Service Worker
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
.then(function(registration) {
console.log('Service Worker 注册成功:', registration);
})
.catch(function(error) {
console.log('Service Worker 注册失败:', error);
});
}
// 在 Service Worker 中缓存资源
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open('my-cache').then(function(cache) {
return cache.addAll([
'/',
'/index.html',
'/styles.css',
'/script.js'
]);
})
);
});
// 拦截网络请求并从缓存中返回资源
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request).then(function(response) {
return response || fetch(event.request);
})
);
});
我们首先尝试在浏览器中注册一个 Service Worker,并指定了要缓存的资源。然后,在 Service Worker 中,我们通过监听 install
事件来缓存这些资源,并在 fetch
事件中拦截网络请求,从缓存中返回资源。这样,即使在离线时,页面仍能够加载所需资源。
生命周期
Service Worker 的生命周期与 web 页面完全分离。它包括以下几个阶段:
- 下载
- 安装
- 激活
1.下载
用户首次访问service worker控制的网站或页面时,service worker会立刻被下载。浏览器会下载包含 Service Worker 的 .js 文件。
2.安装
需要在网页进行注册来安装,安装前需要检查是否支持 serviceWorker,如果支持,每次页面加载时就调用 register(),浏览器将会判断是否已注册。 register() 方法的一个重要细节是 Service Worker 文件的位置。在本例中,可以看到 Service Worker 文件位于域的根目录,这意味着 Service Worker 范围将是这个域下的。换句话说,这个 Service Worker 将为这个域中的所有内容接收 fetch 事件。如果我们在 /example/sw/sw.js 注册 Service Worker 文件,那么 Service Worker 只会看到以 /example/ 开头的页面的 fetch 事件(例如 /example/page1/、/example/page2/)。
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/sw/sw.js').then(function(registration) {
// 注册成功
console.log('ServiceWorker registration successful with scope: ', registration.scope);
}, function(err) {
// 注册失败
console.log('ServiceWorker registration failed: ', err);
});
});
}
注册成功后,install 事件会被触发,将会调用caches.open() 和我们想要的缓存名称, 之后调用 cache.addAll() 并传入文件数组。 这是一个promise 链( caches.open() 和 cache.addAll() )。 event.waitUntil() 方法接受一个promise,并使用它来知道安装需要多长时间,以及它是否成功。 如果成功缓存了所有文件,那么将安装 Service Worker。如果其中的一个文件下载失败,那么安装步骤将失败。如果缓存文件列表过长,将会增大失败的几率。
代码语言:javascript复制var CACHE_NAME = 'my-cache';
var urlsToCache = [
'/',
'/styles/main.css',
'/script/main.js'
];
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open(CACHE_NAME)
.then(function(cache) {
console.log('Opened cache');
return cache.addAll(urlsToCache);
})
);
});
3)激活
接下来就是进入激活状态:Activate。 在这个状态可以更新 Service Worker。
- 用户导航至站点时,浏览器会尝试在后台重新下载定义 Service Worker 的脚本文件。 如果 Service Worker 文件与其当前所用文件存在字节差异,则将其视为新 Service Worker。
- 新 Service Worker 将会启动,且将会触发 install 事件。
- 旧 Service Worker 仍控制着当前页面,因此新 Service Worker 将进入 waiting 状态。
- 当网站上当前打开的页面关闭时,旧 Service Worker 将会被终止,新 Service Worker 将会取得控制权。
- 新 Service Worker 取得控制权后,将会触发其 activate 事件。
self.addEventListener('activate', function(event) {
var cacheAllowlist = ['pages-cache-v1', 'blog-posts-cache-v1'];
event.waitUntil(
caches.keys().then(function(cacheNames) {
return Promise.all(
cacheNames.map(function(cacheName) {
if (cacheAllowlist.indexOf(cacheName) === -1) {
return caches.delete(cacheName);
}
})
);
})
);
});
缓存与请求响应优化
策略
- 缓存优先
- 网络优先
- 仅使用缓存
- 仅使用网络
- 速度优先
一旦安装了 Service Worker 并且用户导航到其他页面或刷新当前页面,Service Worker 将开始监听 fetch 事件。
缓存优先策略的工作流程:首先,它会监听浏览器的 fetch 事件,拦截原始的请求。接着,它会检查缓存中是否存在即将请求的资源,如果存在,则直接返回缓存中的资源。然后,它会发起远程请求来获取最新资源,将资源缓存起来,并返回给页面。
代码语言:javascript复制self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request)
.then(function(response) {
if (response) {
return response;
}
var fetchRequest = event.request.clone();
return fetch(fetchRequest).then(
function(response) {
if(!response || response.status !== 200 || response.type !== 'basic') {
return response;
}
var responseToCache = response.clone();
caches.open(CACHE_NAME)
.then(function(cache) {
cache.put(event.request, responseToCache);
});
return response;
}
);
})
);
});
什么是 PWA?
PWA 是一种使用现代 Web API 和传统的渐进性增强策略来创建跨平台 Web 应用程序的方法。它结合了 Web 应用程序的可发现性、易安装性和可链接性,以及原生应用的性能和交互体验。
优点
渐进性
PWA 适用于所有浏览器,因为它是以渐进性增强作为宗旨开发的,用户无需担心浏览器兼容性问题。
连接无关性
PWA 可以在离线或网络较差的情况下正常访问,依赖于 Service Worker 技术,这使得用户体验更稳定。
类原生应用
由于是在 App Shell 模型基础上开发,PWA 具有与原生应用相似的用户交互体验,为用户提供了更高的满意度。
持续更新
PWA 始终保持最新状态,无需用户手动更新,这消除了版本管理的烦恼。
安全性
通过 HTTPS 协议提供服务,保护用户数据不被窥探,并确保内容不被篡改。
可索引
PWA 的 manifest 文件和 Service Worker 可以被搜索引擎索引,提高应用的可见性。
黏性
通过推送离线通知等功能,PWA 可以吸引用户回流,提高用户参与度。
可安装
用户可以将常用的 Web App 添加到桌面,无需前往应用商店下载安装,提高了可用性。
可链接
通过简单的链接即可分享内容,无需下载和安装,便捷实用。
缺点
对系统功能的访问权限较低
与原生应用相比,PWA 对设备的系统功能访问权限相对较低,某些高级功能可能受到限制。
没有统一的审查标准
与应用商店不同,PWA 没有统一的审查标准,这可能导致一些质量参差不齐的应用进入市场。
核心技术
3. 核心技术
- Web App Manifest Web App Manifest(Web 应用程序清单)概括地说是一个以 JSON 形式集中书写页面相关信息和配置的文件。
{
"name": "My PWA",
"short_name": "PWA",
"start_url": "/index.html",
"display": "standalone",
"background_color": "#ffffff",
"theme_color": "#007bff",
"icons": [
{
"src": "/icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
]
}
- start_url 可以设置启动网址
- icons 会帮我萌设置各个分辨率下页面的图标
- background_color 会设置背景颜色, Chrome 在网络应用启动后会立即使用此颜色,这一颜色将保留在屏幕上,直至网络应用首次呈现为止
- theme_color 会设置主题颜色
- display 设置启动样式
- Service Worker
- Notifications API 通知API
- Push API 推送API 推送 API 可以用来从服务端推送新的内容而无需客户端介入,它是由应用的 Service Worker 来实现的;通知功能则可以通过 Service Worker 来向用户展示一些新信息,或者至少提醒用户应用已经更新了某些功能。