Progressive Web App(PWA) 初识

208 阅读6分钟

Progressive Web App(PWA)

PWA的中文名叫做渐进式网页应用,是提升 Web App 的体验的一种新方法,能给用户原生应用的体验

PWA 不是特指某一项技术,而是应用了多项技术的 Web App。其核心技术包括 App Manifest、Service Worker、Web Push,等等

PWA具有以下特点:

  • 可发现: 可以通过搜索引擎发现。
  • 可安装: 可以出现在设备的主屏幕。
  • 可链接: 可以简单地通过 URL 分享。
  • 独立于网络: 可以在离线状态或者是在网速很差的情况下运行。
  • 渐进式: 在老版本的浏览器仍旧可以使用,在新版本的浏览器上可以使用全部功能。
  • 可重入: 无论何时有新的内容,都可以发送通知。
  • 响应式: 在任何具有屏幕和浏览器的设备上可以正常使用——包括手机、平板电脑、笔记本、电视、冰箱等。
  • 安全:在用户、应用和服务器之间的连接是安全的,第三方无法访问你的敏感数据。

使用PWA很简单,只需要在HTML中的head中使用link标签引用一个manifest.json文件即可。

manifest

添加应用至主屏幕

显示应用安装横幅的条件

浏览器在 PWA 站点满足以下条件时会自动显示横幅:

  • 站点部署 manifest.json,该文件需配置如下属性:
    • short_name (用于主屏幕显示)
    • name (用于安装横幅显示)
    • icons (其中必须包含一个 mime 类型为 image/png 的图标声明)
    • start_url (应用启动地址)
    • display (必须为 standalone 或 fullscreen)
  • 站点注册 Service Worker。
  • 站点支持 HTTPS 访问。
  • 站点在同一浏览器中被访问至少两次,两次访问间隔至少为 5 分钟。

manifest.json 的配置如下:

{
    "short_name": "短名称",
    "name": "这是一个完整名称",
    "icon": [
        {
            "src": "../../images/logo-144x144.png",
            "type": "image/png",
            "sizes": "144x144"
        }
    ],
    "start_url": "./index.html"
}

显示原生应用安装横幅的条件

浏览器在 PWA 站点满足以下条件时会自动显示横幅:

  • 站点部署 manifest.json,该文件需配置如下属性:
    • short_name (用于主屏幕显示)
    • name (用于安装横幅显示)
    • icons (其中必须包含一个 192x192 且 mime 类型为 image/png 的图标声明)
    • 包含原生应用相关信息的 related_applications 对象
  • 站点注册 Service Worker。
  • 站点支持 HTTPS 访问。
  • 站点在同一浏览器中被访问至少两次,两次访问间隔至少为 2 天。

其中 related_applications 的定义如下:

related_applications: Array. 关联应用列表 AppInfo 的属性值包括:

platform: {string} 应用平台 id: {string} 应用id

"related_applications": [
    {
        "platform": "play",
        "id": "com.baidu.samples.apps.iosched"
    }
]

常用配置的含义:

  • name:应用的名称
  • short_name:应用的简称
  • start_url:应用的启动页
  • display:应用的显示模式,有以下几种模式:
    • fullscreen:全屏模式
    • standalone:独立模式
    • minimal-ui:最小化模式
    • browser:浏览器模式
  • background_color:应用的背景颜色
  • theme_color:应用的主题颜色
  • description:应用的描述
  • icons:应用的图标,可以配置多个图标,每个图标都有自己的尺寸和类型
  • src:图标的路径
  • sizes:图标的尺寸
  • type:图标的类型

如:squoosh.app/

Service Worker

ServiceWorker 是一个运行在浏览器背后的独立线程,它拥有访问网络的能力,可以用来实现缓存、消息推送、后台自动更新等功能,甚至可以用来实现一个完整的 Web 服务器。

Service Worker 的生命周期

  • parsed: 注册完成, 脚本解析成功, 尚未安装
  • installing: 对应 Service Worker 脚本 install 事件执行, 如果事件里有 - event.waitUntil() 则会等待传入的 Promise 完成才会成功
  • installed(waiting): 页面被旧的 Service Worker 脚本控制, 所以当前的脚本尚未激活。可以通过 self.skipWaiting() 激活新的 Service Worker
  • activating: 对应 Service Worker 脚本 activate 事件执行, 如果事件里有 event.waitUntil() 则会等待这个 Promise 完成才会成功。这时可以调用 Clients.claim() 接管页面
  • activated: 激活成功, 可以处理 fetch, message 等事件
  • redundant: 安装失败, 或者激活失败, 或者被新的 Service Worker 替代掉

Service Worker 注册

if (navigator.serviceWorker) {
  navigator.serviceWorker.register('service-worker.js')
  .then(function(registration) {
    console.log('service worker 注册成功');
  }).catch(function (err) {
    console.log('servcie worker 注册失败');
  });
}

Service Worker 拦截

this.addEventListener('fetch', function(event) {
  event.respondWith(
    // magic goes here
  );
});

Service Worker 更新缓存

  • 更新sw.js文件,一旦浏览器发现安装使用的sw.js是不同的(通过计算hash值),浏览器就会重新安装service worker,你可以在安装激活的过程中清空之前的缓存,这样浏览器就会使用服务器上最新的资源。

  • 对资源文件进行版本控制,就像我上面的例子一样你可以用style-2.css来代替style-1.css,这样service worker就会使用新的资源并缓存它。当然版本号不应该这么简单,最好是使用文件的内容+修改时间+大小的hash值来作为版本号。

缓存区别

  • 浏览器缓存:
    • 分为强缓存和协商缓存,其共同点都是通过设置 HTTP Header 实现
    • 当没有网络的时候,应用无法访问,因为 HTML 页面总得去服务器获取。 缓存不可编程,无法通过 JS 来精细地对缓存进行增删改查。
  • 应用缓存(Application Cache):
    • 不能选择更新哪些资源
    • 当manifest文件更新了,所有指定资源都会重新下载
  • Web Worker:
    • 只能服务于新建它的页面,不同页面之间不能共享同一个 Web Worker
    • 当页面关闭时,该页面新建的 Web Worker 也会随之关闭,不会常驻在浏览器中。
  • Service Worker
    • 可精细化控制缓存
    • 不是服务于某个特定页面的,而是服务于多个页面的。(按照同源策略)
    • 会常驻在浏览器中,即便注册它的页面已经关闭,Service Worker 也不会停止。本质上它是一个后台线程,只有你主动终结,或者浏览器回收,这个线程才会结束
优先级: memory > sw > dist

加载速度: memory > dist > sw

service-worker缺点:

  • 无法访问DOM,无法使用同步方法,如localStorage
  • 必须再https环境下使用
  • 需要在作用范围的根目录下创建service-worker,如要将全局的页面进行管理,就需要在根目录下注册
  • 速度比浏览器缓存慢
  • 在存在memory cache的情况下,无法更新缓存

通知推送

通知

它能够脱离推送 API 单独工作

通知-请求授权

var button = document.getElementById("notifications");
button.addEventListener('click', function(e) {
    Notification.requestPermission().then(function(result) {
        if(result === 'granted') {
            randomNotification();
        }
    });
});

创建一个通知

function randomNotification() {
    var randomItem = Math.floor(Math.random()*games.length);
    var notifTitle = games[randomItem].name;
    var notifBody = 'Created by '+games[randomItem].author+'.';
    var notifImg = 'data/img/'+games[randomItem].slug+'.jpg';
    var options = {
        body: notifBody,
        icon: notifImg
    }
    var notif = new Notification(notifTitle, options);
    setTimeout(randomNotification, 30000);
}

推送

通知 (notification),它允许服务器向用户提示一些信息,并根据用户不同的行为进行一些简单的处理

步骤:

  • 注册ServiceWorker获得registration对象
  • 通过registration对象获得PushManager对象
  • 通过PushManager对象订阅消息推送,获得subscription对象
  • 将subscription对象发送给服务器,由服务器保存
  • 服务器通过subscription对象推送消息
  • ServiceWorker通过监听push事件,获得推送的消息
  • ServiceWorker通过showNotification方法,显示消息

在FCM服务器申请 GCMApiKey 用于验证推送消息的身份

离线推送

当浏览器关闭的时候,如果有推送消息,这时推送消息就会被FCM服务器保存起来,等你的网页或者浏览器上线的时候,FCM服务器会通过notificationcallback回调来推送消息到你的网页或者浏览器

PWA 支持检测工具: www.pwabuilder.com/

注:部分图片来自互联网