你在浏览网页的时候,一定会注意到有一些网站在地址栏右侧会显示一个 “加号”,点击这个加号就能安装当前网站的 PWA 应用。是不是很炫酷?我们一起来看一下它的适配实现方法。

什么是PWA

PWA(Progressive web apps,渐进式 Web 应用)运用现代的 Web API 以及传统的渐进式增强策略来创建跨平台 Web 应用程序。

我们平常用浏览器上网,咱们本地有一个代理服务器(就是用来缓存的),把咱们一些常访问的网站的部分资源缓存在本地,等到下一次访问的时候就不用重新 HTTP/HTTPS 请求访问了,加快了访问速度,一段时间后就删除这个缓存。

而 PWA 应用则更近一步,不仅可以自己指定需要缓存的文件和目录,还可以像 App 那样,独立出来、常驻桌面,体验也与传统 App 没有什么大的区别。

废话不多说了,更多的介绍可以去相关网站,都有很详细的讲解:

Google: https://developers.google.com/web/progressive-web-apps/

web.dev: https://web.dev/progressive-web-apps/

mozilla.org: https://developer.mozilla.org/zh-CN/docs/Web/Progressive_web_apps/Introduction

让网站初步支持PWA

PWA 应用开发有着很大的空间,是一个长期的开发过程。但是让网站初步支持、适配 PWA 还是很容易的,主要有以下几步:

网站的HTTPS化

支持 PWA 的必要条件,就是网站必须 HTTPS 化。

然后对网站进行一次检测。你可以使用 Chrome 的 Lighthouse 对网站进行测试。Lighthouse 是一个 chrome 插件,可以告诉你访问的网站是不是支持 PWA,如果不支持应该如何优化。

Chrome 检查 – Lighthouse 页面

还可以使用 web.dev 的测试页面

web.dev 测试页面

没有严重问题,就可以开始准备相关配置文件了!

准备文件

清单如下,需要如下三个文件是必要的:

  • manifest.json(PWA应用信息的文件)
  • sw.js(Service Worker 的配置文件)
  • pwa.png(PWA 应用的图标图片)

1.PWA应用图标

建议制作 192×192px512×512px 这两种尺寸的 .png 格式的图标。

2.manifest.json

manifest.json 文件就是一个结构化数据文件,我是这样配置的:

{
    "name": "筑暻卖萌屋",
    "short_name": "筑暻卖萌屋",
    "description": "Nousbuild Website PWA",
    "icons": [
        {
            "src": "https://www.nousbuild.org/pwa@192.png",
            "sizes": "192x192",
            "type": "image/png",
            "purpose": "any maskable"
        },
        {
            "src": "https://www.nousbuild.org/pwa@512.png",
            "sizes": "512x512",
            "type": "image/png"
        }
    ],
    "background_color": "#FFE8E7", //应用加载之前的背景色,用于应用启动时的过渡
    "theme_color": "#FFE8E7", //主题颜色,用于控制浏览器地址栏着色
    "display": "standalone", // 定义应用的显示方式
    "orientation": "portrait",
    "start_url": "/", // 打开后第一个出现的页面地址
    "scope": "/" // PWA作用域
}

从上到下,一次是 PWA 应用的名称、描述、图标文件、主题色、显示方式、开始页面的链接和 PWA 的作用域。相信这些都很好懂,大家直接改成自己的就行了,这是最最基本的选项,建议都填。

此外还有很多相关参数可以填写,具体可以参考 web.dev。Github 上有一个开源 Web App Manifest 生成器,你还可以在此填写并生成。

3.sw.js

PWA 是通过 ServiceWorker 访问 Cache,所以需要注册 ServiceWorker 工作者,就用 .js 文件来配置:

'use strict'
var cacheStorageKey = 'minimal-pwa-8'
let cacheName = 'pwa-you-website-cache'; // 缓存名字

var cacheList = [ // 所需缓存的文件
  '/',
  "index.html"
]

self.addEventListener('install', function(e) {
  console.log('Cache event!')
  e.waitUntil(
    // 安装服务者时,对需要缓存的文件进行缓存
    caches.open(cacheStorageKey).then(function(cache) {
      console.log('Adding to Cache:', cacheList)
      return cache.addAll(cacheList)
    }).then(function() {
      console.log('Skip waiting!')
      return self.skipWaiting()
    })
  )
})

self.addEventListener('activate', function(e) {
  // 判断地址是不是需要实时去请求,是就继续发送请求
  console.log('Activate event')
  e.waitUntil(
    Promise.all(
      caches.keys().then(cacheNames => {
        return cacheNames.map(name => {
          if (name !== cacheStorageKey) {
            return caches.delete(name)
          }
        })
      })
    ).then(() => {
      console.log('Clients claims.')
      return self.clients.claim()
    })
  )
})

self.addEventListener('fetch', function(e) {
  // 匹配到缓存资源,就从缓存中返回数据
  e.respondWith(
    caches.match(e.request).then(function(response) {
      if (response != null) {
        console.log('Using cache for:', e.request.url)
        return response
      }
      console.log('Fallback to fetch:', e.request.url)
      return fetch(e.request.url)
    })
  )
})

三个必要文件准备好了,我们就开始组装使用。

使用文件

将 manifest.json 文件,引入到每个页面的 <header> 中:

<link rel="manifest" href="manifest.json">

先判断浏览器是否支持 PWA,再在每个页面有条件的引入 sw.js 文件:

<script type="text/javascript">
	if (navigator.serviceWorker != null) {
		navigator.serviceWorker.register('sw.js')
		.then(function(registration) {
			console.log('Registered events at scope: ', registration.scope);
		});
	}
</script>

这样我们就初步的成功适配了 PWA!

问题排查

可能会有小伙伴们问,为什么我照着这样做了,我的网站右上角还是没有 PWA 应用的 “加号”?

首先你现在 Chrome 的 Application 页面下进行排查。看 Manifest 标签有没有读出你的 PWA 应用信息配置文件:

再到下面一个 Service Workers 标签,看看状态码是不是正常的:

如果有一个有问题,就要自己排查看看哪里出错了。如果都正常,但是就是没有 PWA 应用的 “加号”,那可能就是因为这一条:用户需要至少浏览网站两次,并且两次访问间隔在五分钟之上。

Service Workers 的改进

如果你的网站是使用动态语言(例如.php)开发的,那你在访问网站或者某些页面的时候可能会出错,浏览器会提示某页面 “已永久性地移动到了新网址” 或者 “网页可能暂时无法连接” 这样的提示,必须要清空掉 PWA 的缓存,才可以访问,然后又出现这样的错误。

清空 PWA 的缓存

这应该是因为动态文件缓存的原因,导致 PWA 在缓存中没有找到 php 文件。这也是咱们 sw.js 写的不严谨,只是初步适配。改进的 sw.js 可以这样写:

'use strict';
 
const cacheName = 'pwa-you-website-cache'; // 缓存名字
const startPage = 'https://www.nousbuild.org/'; // 首页地址
const offlinePage = 'https://www.nousbuild.org/';// 离线首页地址
const filesToCache = [startPage, offlinePage];

// 不缓存的目录,比如 WordPress 的 wp-admin 和 wp-login 文件夹
const neverCacheUrls = [/wp-admin/,/wp-login/,/preview=true/];

// 之后的代码可以不用修改
// Install
self.addEventListener('install', function(e) {
	console.log('PWA service worker installation');
	e.waitUntil(
		caches.open(cacheName).then(function(cache) {
			console.log('PWA service worker caching dependencies');
			filesToCache.map(function(url) {
				return cache.add(url).catch(function (reason) {
					return console.log('PWA: ' + String(reason) + ' ' + url);
				});
			});
		})
	);
});

// Activate
self.addEventListener('activate', function(e) {
	console.log('PWA service worker activation');
	e.waitUntil(
		caches.keys().then(function(keyList) {
			return Promise.all(keyList.map(function(key) {
				if ( key !== cacheName ) {
					console.log('PWA old cache removed', key);
					return caches.delete(key);
				}
			}));
		})
	);
	return self.clients.claim();
});

// Fetch
self.addEventListener('fetch', function(e) {
	
	// Return if the current request url is in the never cache list
	if ( ! neverCacheUrls.every(checkNeverCacheList, e.request.url) ) {
	  console.log( 'PWA: Current request is excluded from cache.' );
	  return;
	}
	
	// Return if request url protocal isn't http or https
	if ( ! e.request.url.match(/^(http|https):\/\//i) )
		return;
	
	// Return if request url is from an external domain.
	if ( new URL(e.request.url).origin !== location.origin )
		return;
	
	// For POST requests, do not use the cache. Serve offline page if offline.
	if ( e.request.method !== 'GET' ) {
		e.respondWith(
			fetch(e.request).catch( function() {
				return caches.match(offlinePage);
			})
		);
		return;
	}
	
	// Revving strategy
	if ( e.request.mode === 'navigate' && navigator.onLine ) {
		e.respondWith(
			fetch(e.request).then(function(response) {
				return caches.open(cacheName).then(function(cache) {
					cache.put(e.request, response.clone());
					return response;
				});  
			})
		);
		return;
	}

	e.respondWith(
		caches.match(e.request).then(function(response) {
			return response || fetch(e.request).then(function(response) {
				return caches.open(cacheName).then(function(cache) {
					cache.put(e.request, response.clone());
					return response;
				});  
			});
		}).catch(function() {
			return caches.match(offlinePage);
		})
	);
});

// Check if current url is in the neverCacheUrls list
function checkNeverCacheList(url) {
	if ( this.match(url) ) {
		return false;
	}
	return true;
}

这样子就可以解决动态网页的问题。

Chrome 93 的问题修复

从Chrome 89(将于2021年3月稳定)开始,如果PWA在离线状态下未提供有效的响应,则会在开发人员工具的“问题”标签下显示一条消息。该 beforeinstallprompt 事件和浏览器内安装提示仍将提供。

提示信息

而从Chrome 93(将于2021年8月稳定)开始,将强制执行更新的安装条件。如果PWA在脱机时未提供有效的响应,它将不再通过可安装性检查,beforeinstallprompt 将不会触发该事件,并且不会显示浏览器内安装提示。这样我们之前写的 sw.js 文件就无效了。

详见Google 文档:改进渐进式Web App脱机支持检测

最新的 sw.js 改进文件如下:

// PWA应用的版本
const CACHE_VERSION = '1.0.0';

// 常缓存文件,可自由添加
const BASE_CACHE_FILES = [
    'https://www.nousbuild.org/pwa@192.png',
    'https://www.nousbuild.org/pwa@512.png'
];

// 离线缓存首页
const OFFLINE_CACHE_FILES = [
    'https://www.nousbuild.org/',
];

// 未找到缓存文件时返回
const NOT_FOUND_CACHE_FILES = [
    'https://www.nousbuild.org/',
];

// 离线缓存首页和未找到缓存文件时返回
const OFFLINE_PAGE = 'https://www.nousbuild.org/';
const NOT_FOUND_PAGE = 'https://www.nousbuild.org/';

/**
	后续代码文件不用修改
    完整 sw.js 下载地址:https://oss.nousbuild.com/download/sw-example.zip
    只需修改上述开头几行配置信息即可
 **/

新写的 sw.js 增加了缓存列表,适配了 Chrome 93,你只需修改相关的链接即可使用。

浏览器的支持

最后说一下浏览器的支持。其实 Microsoft Edge 可以直接将你的网站转成 PWA 应用,而 Chrome 必须适配以后,才会有这个选项。Safari 的支持比较一般。

苹果的 Safari 如果仅 manifest.json 配置可能还并不能兼容,还要通过 <meta><link> 进行设置。可设置如下:

<!-- PWA应用名称 -->
<meta name="apple-mobile-web-app-title" content="PWA应用名称">

<!-- 是否隐藏地址栏 -->
<meta name="apple-mobile-web-app-capable" content="yes">

<!-- 修改状态栏颜色 -->
<meta name="apple-mobile-web-app-status-bar-style" content="black">

iOS 版的 Safari 不支持显示 PWA 适配的提示,只能自己引导用户将网站添加到主屏幕。

2 thoughts on “让你的网站初步适配PWA

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

Next Post

带有防盗链的图片获取方法

周五 2月 12 , 2021
我们在网上总会保存一些自己喜欢的图片,当然很多时候 […]