什么是 PWA
PWA即 Progressive Web App,渐进式 Web 应用,是由 Google 推广的一种 Web 应用标准,希望借此来结合 Web 与原生 App 的优势。
具有可安装,可离线使用的特性,同时兼具 Web 的轻便和可快速触达。
安装后的 PWA拥有独立的 App运行窗口,作为用户来说,这可以让 Web应用更接近于原生 App。
在桌面浏览器中安装
在移动端浏览器中安装
虽然仅提供作为描述文件的
manifest.json
就可获得可安装特性。但 PWA至少还需要一个 service worker 来提供离线使用能力才能算的上合格。另外最近不胜其烦的网站通知请求,也是为 PWA 提供了
Notification Api
使其能更接近原生 APP。如何构建
PWA包括三个必要条件:
https
服务,在localhost
上除外。
- 至少一个 service worker ,缓存应用的网络请求,控制是否缓存应用的网络请求
manifes.json
应用清单,用于描述应用的信息和安装时的 icon。
Service Worker
service workers 本质上充当 Web 应用程序、浏览器与网络(可用时)之间的代理服务器。这个 API 旨在创建有效的离线体验,它会拦截网络请求并根据网络是否可用来采取适当的动作、更新来自服务器的的资源。它还提供入口以推送通知和访问后台同步 API。
service worker 是一个注册在指定源和路径下的事件驱动 worker。它采用 JavaScript 控制关联的页面或者网站,拦截并修改访问和资源请求,细粒度地缓存资源。你可以完全控制应用在特定情形(最常见的情形是网络不可用)下的表现。
service worker 运行在 worker 上下文,因此它不能访问 DOM。相对于驱动应用的主 JavaScript 线程,它运行在其他线程中,所以不会造成阻塞。它设计为完全异步,同步 API(如
XHR
和 localStorage
)不能在 service worker 中使用。- 在 public新建
sw.js
( service worker 必须在访问域名的根路径下)。
const version = '1.0.2', CACHE = version + '::ZiyiMember', installFilesEssential = [ '/', '/manifest.json', '/favicon.ico', '/logo.jpeg', ]; // install static assets function installStaticFiles() { return caches.open(CACHE) .then(cache => { return cache.addAll(installFilesEssential); }); } function clearOldCaches() { return caches.keys() .then(keylist => { return Promise.all( keylist .filter(key => key !== CACHE) .map(key => caches.delete(key)) ); }); } self.addEventListener('install', event => { event.waitUntil( installStaticFiles() .then(() => self.skipWaiting()) ); }); self.addEventListener('activate', event => { event.waitUntil( clearOldCaches() .then(() => self.clients.claim()) ); }); self.addEventListener('fetch', event => { if (event.request.method !== 'GET') return; let url = event.request.url; event.respondWith( caches.open(CACHE) .then(cache => { return cache.match(event.request) .then(response => { if (response) { return response; } return fetch(event.request) .then(newreq => { console.log('network fetch: ' + url); if (newreq.ok) cache.put(event.request, newreq.clone()); return newreq; }) .catch(()=>null); }); }) ); });
- 在 Web加载时,注册 service worker。
ServiceWorkerContainer.register(scriptURL, options) .then((ServiceWorkerRegistration)=>{ ... })
manifest.json
manifest.json
是每个 WebExtension 唯一必须包含的元数据文件。通过使用
manifest.json
,您可以指定扩展的基本元数据,例如名称和版本,还可以指定扩展各个方面的功能(例如后台脚本,内容脚本和某些浏览器行为)。它是一个允许使用 "
//
" 撰写单行注释的、特殊的 JSON 文件。- 编写
manifest.json
文件
示例字段:
{ "browser_specific_settings": { "gecko": { "id": "[email protected]", "strict_min_version": "42.0" } }, "background": { "scripts": ["jquery.js", "my-background.js"], }, "browser_action": { "default_icon": { "19": "button/geo-19.png", "38": "button/geo-38.png" }, "default_title": "Whereami?", "default_popup": "popup/geo.html" }, "commands": { "toggle-feature": { "suggested_key": { "default": "Ctrl+Shift+Y", "linux": "Ctrl+Shift+U" }, "description": "Send a 'toggle-feature' event" } }, "content_security_policy": "script-src 'self' https://example.com; object-src 'self'", "content_scripts": [ { "exclude_matches": ["*://developer.mozilla.org/*"], "matches": ["*://*.mozilla.org/*"], "js": ["borderify.js"] } ], "default_locale": "en", "description": "...", "icons": { "48": "icon.png", "96": "[email protected]" }, "manifest_version": 2, "name": "...", "page_action": { "default_icon": { "19": "button/geo-19.png", "38": "button/geo-38.png" }, "default_title": "Whereami?", "default_popup": "popup/geo.html" }, "permissions": ["webNavigation"], "version": "0.1", "user_scripts": { "api_script": "apiscript.js", }, "web_accessible_resources": ["images/my-image.png"] }
- 在
head
中引入
<link rel="manifest" href="/manifest.json" />
为 Next.js
项目提供 PWA支持
通过
next-pwa
这个库,可以快速为 Next.js
项目提供PWA支持。但是目前使用中发现
SSR
方案将请求代码放在服务端的做法,无法支持请求缓存。而且在安装为 PWA后,请求响应的接口时也会报错。分析 PWA 是否可用
对于 Chrome 浏览器,在开发者工具中有 Lighthouse 工具可以对 Web 页面的性能、无障碍能力、SEO、PWA 甚至是 框架使用做出建议。
当 PWA 配置不生效的时候,可以使用 Lighthouse 分析,根据建议作出改进。