如何实现 PWA 全局离线化
在当今互联网发展日新月异的时代,网页应用程序已成为人们使用电脑和手机时最常用的工具之一。然而,即使在今天,仍然有很多用户会遭遇网络断连或是网络不稳定的困扰,这极大地影响了网页程序的使用体验。因此,全局离线化已成为网页应用程序保证稳定运行的必要措施之一。在本文中,我们将详细介绍如何实现 PWA 全局离线化,包括概念、实现方法和示例代码。
概念
PWA(progressive web app)指的是在 web 应用中利用现代 Web 技术和标准,使其能够和原生应用程序相一致的 Web 应用程序。全局离线化是 PWA 的其中一个关键特性,它使 Web 应用程序在没有办法连接到 Internet 的情况下也能运行。全局离线可以分为两种情况,分别是第一次加载和后续加载的全局离线。
在第一次加载时,浏览器会将应用程序缓存下来,即使下次访问应用程序时没有网络连接,应用程序也可以从缓存中加载。在后续加载时,则可以通过更新缓存来保证应用程序代码的新鲜性。总之,全局离线化是在不可连接互联网的情况下继续使用网页应用程序的能力。
实现方法
实现全局离线化主要有以下几个步骤:
- 编写 Service Worker
Service Worker 是一个独立于浏览器主进程的子线程,它可以处理所有离线和网络请求。在 PWA 中,Service Worker 负责实现全局离线功能。具体来说,它需要监听应用程序缓存事件,如向缓存中添加资源、更新缓存以及从缓存中获取资源。可以通过以下代码来注册 Service Worker。
if ('serviceWorker' in navigator) { window.addEventListener('load', () => { navigator.serviceWorker.register('/sw.js').then((registration) => { console.log('SW registered'); }); }); }
- 编写缓存机制
Service Worker 需要缓存应用程序代码和其他资源,以便在没有网络连接的情况下运行。缓存支持以下两种方式:
- 缓存资源:Service Worker 可以通过 Cache API 来缓存静态资源。例如,可以缓存首页、CSS、JS 等资源。
self.addEventListener('install', (event) => { event.waitUntil( caches.open('static').then((cache) => { return cache.addAll([ '/index.html', '/assets/css/main.css', '/assets/js/main.js' ]); }) ); });
- 缓存动态响应:Service Worker 可以通过 Fetch API 来缓存动态响应。例如,在每次响应时将数据存储在缓存中,以便在没有网络连接的情况下使用缓存数据。
self.addEventListener('fetch', (event) => { event.respondWith( caches.match(event.request).then((cachedResponse) => { return cachedResponse || fetch(event.request).then((response) => { caches.open('dynamic').then((cache) => { cache.put(event.request, response.clone()); }); return response; }) }) ); });
- 处理离线情况
当用户在没有网络连接的情况下访问应用程序时,Service Worker 必须能够处理请求并返回缓存的数据。可以通过以下代码来实现:
self.addEventListener('fetch', (event) => { event.respondWith( caches.match(event.request).then((cachedResponse) => { return cachedResponse || fetch(event.request).then((response) => { caches.open('dynamic').then((cache) => { cache.put(event.request, response.clone()); }); return response; }) }) ); });
- 更新缓存
在应用程序中更新缓存很重要,因为用户在使用应用程序时需要体验到最新的内容。更新缓存需要在 Service Worker 中实现以下两个步骤:
- 删除旧的缓存:Service Worker 需要删除旧的缓存以避免缓存冲突。
self.addEventListener('activate', (event) => { event.waitUntil( caches.keys().then((cacheNames) => { return Promise.all( cacheNames.filter((cacheName) => { return cacheName !== 'static' && cacheName !== 'dynamic'; }).map((cacheName) => { return caches.delete(cacheName); }) ); }) ); });
- 更新缓存:Service Worker 需要更新缓存以加载最新的应用程序内容。
function updateDynamicCache(request, response) { return caches.open('dynamic').then((cache) => { if (response.status === 200) { cache.put(request, response.clone()); } return response; }) }
示例代码
以下是完整的代码示例,包括 Service Worker 的注册、缓存机制、处理离线情况和更新缓存。请在支持 Service Worker 的浏览器中测试,以获取最佳的使用体验。
sw.js
const CACHE_STATIC_NAME = 'static-v1'; const CACHE_DYNAMIC_NAME = 'dynamic-v1';
self.addEventListener('install', (event) => { event.waitUntil( caches.open(CACHE_STATIC_NAME).then((cache) => { console.log('[Service Worker] Precaching app shell'); cache.addAll([ '/', '/index.html', '/assets/css/main.css', '/assets/js/main.js' ]); }) ); });
self.addEventListener('activate', (event) => { event.waitUntil( caches.keys().then((keyList) => { return Promise.all(keyList.map((key) => { if (key !== CACHE_STATIC_NAME && key !== CACHE_DYNAMIC_NAME) { console.log('[Service Worker] Removing old cache', key); return caches.delete(key); } })); }) ); });
self.addEventListener('fetch', (event) => { event.respondWith( caches.match(event.request).then((response) => { if (response) { return response; } else { return fetch(event.request).then((res) => { return caches.open(CACHE_DYNAMIC_NAME).then((cache) => { cache.put(event.request.url, res.clone()); return res; }) }).catch(() => { return caches.open(CACHE_STATIC_NAME).then((cache) => { return cache.match('/offline.html'); }); }); } }) ); });
index.html
Hello World!
<script></script> <script> if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/sw.js'); } </script> </body> </html>main.js
console.log('Hello World!');
main.css
body { font-family: sans-serif; }
h1 { color: #333; }
offline.html
You are offline!
</body> </html>结论
本文介绍了 PWA 的全局离线化特性,以及如何实现全局离线化功能。由于全局离线化可以提高应用程序的可用性和稳定性,并提高用户的体验,因此这个功能在现代 Web 应用程序中很重要。希望这篇文章能帮助读者更好地理解如何使用 Service Worker 来实现 PWA 的全局离线化功能。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/66ed83b5bc9e1890c5e1e323