最近next项目有使用pwa技术,使用起来也不复杂,目前浏览器的兼容性也比较良好
Service Worker是浏览器中独立于网页运行的脚本,而PWA(渐进式Web应用程序)是一种Web应用程序,其外观和感觉类似于原生应用程序。在讨论Service Worker与PWA之前,让我们先简要了解一下Web Worker。
Web Worker 是浏览器内置的线程,用于执行非阻塞事件循环的 JavaScript 代码。由于 JavaScript 是单线程语言,一次只能处理一个任务。复杂任务的出现可能导致主线程被阻塞,严重影响用户体验。Web Worker 的作用是允许主线程创建 worker 线程,使它们可以同时运行。Worker 线程主要负责处理复杂的计算任务,然后将结果返回给主线程。简而言之,worker 线程执行复杂计算,同时保持页面(主线程)的流畅性,不会造成阻塞。
Web Worker 有三种主要类型:
分配给 Worker 线程运行的脚本文件必须与主线程的脚本文件同源,通常都应该放在同一项目下。
Web Workers 无法访问某些关键的 JavaScript 特性,包括:
1 DOM(因为这可能导致线程不安全)
2 window 对象
3 document 对象
4 parent 对象
出于安全考虑,worker 线程无法读取本地文件。它们加载的脚本必须来自网络,并且必须与主线程的脚本同源。
Service Worker(服务工作线程)是一种在浏览器背后运行的脚本,用于提供强大的离线和缓存功能,以改善 Web 应用程序的性能和可靠性。它是渐进式网络应用程序(Progressive Web App,PWA)的关键组成部分,可以让 Web 应用程序更像本地应用程序,即使在离线状态下也能正常工作。Service Worker 是 Web 开发中的一个强大工具,它使开发人员能够更好地控制和管理 Web 页面的资源缓存、网络请求和响应,从而提供更快速、更稳定的用户体验。
Service Worker 提供了许多重要功能和优点,其中包括:
Service Worker 可以缓存 Web 应用程序的资源,使其在断网或低网络质量环境下仍能够加载和运行。这意味着用户可以随时访问应用程序,无需依赖网络连接。
通过将资源缓存在本地,Service Worker 可以显著提高 Web 页面的加载速度。它可以从缓存中获取资源,而无需每次都从服务器重新下载。
Service Worker 允许在后台执行任务,例如数据同步或推送通知。这使得应用程序可以在不干扰用户的情况下执行一些重要的操作。
Service Worker 受同源策略的限制,因此它可以提供更安全的资源缓存和请求处理。它还可以用于拦截和处理恶意请求。
Service Worker 具有推送通知功能,可以通过浏览器向用户发送实时通知,提高用户参与度和留存率。
// 注册 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 页面完全分离。它包括以下几个阶段:
用户首次访问service worker控制的网站或页面时,service worker会立刻被下载。浏览器会下载包含 Service Worker 的 .js 文件。
需要在网页进行注册来安装,安装前需要检查是否支持 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。如果其中的一个文件下载失败,那么安装步骤将失败。如果缓存文件列表过长,将会增大失败的几率。
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);
})
);
});
接下来就是进入激活状态:Activate。 在这个状态可以更新 Service Worker。
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 事件,拦截原始的请求。接着,它会检查缓存中是否存在即将请求的资源,如果存在,则直接返回缓存中的资源。然后,它会发起远程请求来获取最新资源,将资源缓存起来,并返回给页面。
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 是一种使用现代 Web API 和传统的渐进性增强策略来创建跨平台 Web 应用程序的方法。它结合了 Web 应用程序的可发现性、易安装性和可链接性,以及原生应用的性能和交互体验。
PWA 适用于所有浏览器,因为它是以渐进性增强作为宗旨开发的,用户无需担心浏览器兼容性问题。
PWA 可以在离线或网络较差的情况下正常访问,依赖于 Service Worker 技术,这使得用户体验更稳定。
由于是在 App Shell 模型基础上开发,PWA 具有与原生应用相似的用户交互体验,为用户提供了更高的满意度。
PWA 始终保持最新状态,无需用户手动更新,这消除了版本管理的烦恼。
通过 HTTPS 协议提供服务,保护用户数据不被窥探,并确保内容不被篡改。
PWA 的 manifest 文件和 Service Worker 可以被搜索引擎索引,提高应用的可见性。
通过推送离线通知等功能,PWA 可以吸引用户回流,提高用户参与度。
用户可以将常用的 Web App 添加到桌面,无需前往应用商店下载安装,提高了可用性。
通过简单的链接即可分享内容,无需下载和安装,便捷实用。
与原生应用相比,PWA 对设备的系统功能访问权限相对较低,某些高级功能可能受到限制。
与应用商店不同,PWA 没有统一的审查标准,这可能导致一些质量参差不齐的应用进入市场。
{
"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"
}
]
}