离线後备页面提供用户在网路不稳定的情况下,一个备援的显示页面。
在过去的网站大多由服务器提供,所以断线的情况下原则上就是什麽都没有,近几年 JavaScript 相关的进步以及 SPA 观念开始盛行後,前端能够掌握的事情也越来越多,离线後备页面 (offline fallback page) 一个有名的例子就是 Chrome 在断线状态下的小恐龙游戏。
图片来源: https://web.dev/
对 Progressive Web App 来说,目前最佳的实作方式就是透过 service worker 搭配 Cache Storage API 来提供用户最佳的离线操作体验。
接下来会示范一个简单的情境,当用户网路断线时,会自动开启离线後备页面,页面中一方面提供重试的按钮,另一方面也透过程序实作当网路恢复时自动连线并切换回正常的页面。
Google 的这个范例主要会有三个页面加上 service worker 实作:
Demo 站台:
https://linyencheng.github.io/pwa-offline-fallback/
原始码:
https://github.com/LinYenCheng/pwa-offline-fallback/tree/main/docs
// 这次 Cache 存放的名称
const CACHE_NAME = "offline";
// 离线後备页面档名
const OFFLINE_URL = "offline.html";
install
: 命名快取空间为 CACHE_NAME
并加入 OFFLINE_URL
快取,透过 self.skipWaiting()
跳过等待重启生效。self.addEventListener("install", (event) => {
event.waitUntil(
(async () => {
// 使用 CACHE_NAME
const cache = await caches.open(CACHE_NAME);
// Setting {cache: 'reload'} in the new request will ensure that the response
// isn't fulfilled from the HTTP cache; i.e., it will be from the network.
await cache.add(new Request(OFFLINE_URL, { cache: "reload" }));
})()
);
// Force the waiting service worker to become the active service worker.
self.skipWaiting();
});
activate
: 透过 self.clients.claim()
让 Service Worker 直接生效self.addEventListener("activate", (event) => {
event.waitUntil(
(async () => {
// Enable navigation preload if it's supported.
// See https://developers.google.com/web/updates/2017/02/navigation-preload
if ("navigationPreload" in self.registration) {
await self.registration.navigationPreload.enable();
}
})()
);
// Tell the active service worker to take control of the page immediately.
self.clients.claim();
});
fetch
: 遇到连线错误则使用 CACHE_NAME
中的 OFFLINE_URL
的快取self.addEventListener("fetch", (event) => {
// We only want to call event.respondWith() if this is a navigation request
// for an HTML page.
if (event.request.mode === "navigate") {
event.respondWith(
(async () => {
try {
// First, try to use the navigation preload response if it's supported.
const preloadResponse = await event.preloadResponse;
if (preloadResponse) {
return preloadResponse;
}
// Always try the network first.
const networkResponse = await fetch(event.request);
return networkResponse;
} catch (error) {
// catch is only triggered if an exception is thrown, which is likely
// due to a network error.
// If fetch() returns a valid HTTP response with a response code in
// the 4xx or 5xx range, the catch() will NOT be called.
console.log("Fetch failed; returning offline page instead.", error);
const cache = await caches.open(CACHE_NAME);
const cachedResponse = await cache.match(OFFLINE_URL);
return cachedResponse;
}
})()
);
}
// If our if() condition is false, then this fetch handler won't intercept the
// request. If there are any other fetch handlers registered, they will get a
// chance to call event.respondWith(). If no fetch handlers call
// event.respondWith(), the request will be handled by the browser as if there
// were no service worker involvement.
});
因为是离线後备页面,所以我们会需要把所有的资源都快取起来,其中最简单的方式就是将所有需要的东西都用 inline 的方式写在 html 中,若是想要自己去实作较复杂的快取机制,比较建议取使用 workbox 这套工具。
参考连结:
https://developers.google.com/web/tools/workbox/guides/advanced-recipes#offline_page_only
import {CacheFirst, NetworkFirst, StaleWhileRevalidate} from 'workbox-strategies';
import { registerRoute } from "workbox-routing";
import { CacheFirst } from "workbox-strategies";
import { CacheableResponsePlugin } from "workbox-cacheable-response";
import { RangeRequestsPlugin } from "workbox-range-requests";
// 把 mp4 相关资源都快取起来
registerRoute(
({ url }) => url.pathname.endsWith(".mp4"),
new CacheFirst({
cacheName: "your-cache-name-here",
plugins: [
new CacheableResponsePlugin({ statuses: [200] }),
new RangeRequestsPlugin(),
],
})
);
<<: [Part 3 ] Vue.js 的精随-元件 provide/inject
大家好,我是乌木白,今天想和大家聊聊,如果在学习时或者在写专案时遇到问题该怎麽办? 遇到问题? 今...
昨天我们完成了基础建设,但是有个地方忘记讲到,我现在赶快补充一下! 我们昨天设定归还日期时,一定有人...
循环神经网路(Recurrent neural network, RNN) 首先我们先来介绍循环神经...
前言 以前很经常使用 GitHub 上的各种套件, 不管是大到页面,小到按钮,深到语法,都喜欢用自己...
没有props还可以传资料吗 v-bind和v-on在没有props的情况下一样可以得到父层的资料。...