PWA调研

2,033 阅读3分钟

阅读原文

前言

之前看到有使用ant-design-pro的网站,在服务端更新后,前端会提示刷新页面。但是在调研后发现service-worker.js的更新,只会在重刷页面时检测到,并不会在已经开的tab里被检测到。所以之前看到的可能的情况可能是:有两个tab,一个tab刷新,另外一个tab就能检测service-worker.js。这样的话也不好用。

概述

PWA(Progressive Web App)渐进式网页应用,所谓渐进式,意思就是主张很少,你不用遵守一系列的约束条件。提升 Web App 体验,能给用户原生应用的体验

特性

  1. 跨平台,可安装,独立窗口,沉浸式体验
  2. 消息推送
  3. 缓存
  4. 离线可用

实践

从四个特性来详细讲讲实践效果。

app安装

条件

  1. 在header头里,提供manifest
<link rel="manifest" href="/assets/manifest.json">
  1. 只https可用

  2. manifest.json里提供icon,size > 144

  3. 注册service worker

效果

小结

可以像app一样,安装在桌面,并提供沉浸式的体验,但是感觉和safari浏览器打开有点一样,与safari的区别就是最顶上的mac工具栏没有了。

消息推送

在service-worker.js里监听push事件,并使用self.registration.showNotification(title, options)。需要依赖Notification api。

使用

服务端使用web-push

  1. 在service-worker.js里activate时,向node发送注册subscribe,node把subscription对象存下来.
  2. 在service-worker.js里监听push事件
  3. node调用 webpush.sendNotification(subscription, dataToSend)

效果

略,就是桌面上安装个app一样,

代码示例

  1. 初始化

serivce-worker.js

self.addEventListener('activate', () => {
    self.registration.pushManager.subscribe().then((subscription) => {
        fetch(SERVER_URL, {
            method: "POST",
            headers: {
              'Content-Type': 'application/json',
            },
            body: JSON.stringify(subscription)
        })
    })
})

node


// 初始化subscription
app.post('/save-subscription', async (req, res) => {
    const subscription = req.body;
    await saveToDatabase(subscription);
    res.json({ message: 'success' });
});

// 发送消息
app.get('/send-notification', (req, res) => {
  const subscription = db.subscription;
  const message = 'Hello World';
  webpush.sendNotification(subscription, message);
  res.json({ message: 'message sent' });
})
  1. Push listener
self.addEventListener('push', function(event) {
    const title = 'Push service';
    const options = {
      body: 'Yay it works.',
      icon: 'images/icon.png',
      badge: 'images/badge.png'
    };
  
    event.waitUntil(self.registration.showNotification(title, options));
});
  1. Notification api
document.addEventListener('DOMContentLoaded', function () {
    if (!Notification) {
        console.log('Notification not support')
        return;
    }
    if (Notification.permission !== 'granted') {
        Notification.requestPermission();
    }
});


function notifyMe() {
    if (Notification.permission !== 'granted') {
        Notification.requestPermission();
    } else {
        const notification = new Notification('Notification opened', {
            icon: 'xxx.png',
            body: 'hello',
        });
        notification.onclick = function () {
            window.open('xxx');
        };
    }
}

小结

  1. 单实例做broadcast,可以将缓存里的subscription遍历,并发消息。但是分布式集群很难做,不过也需要像websocket一样,再依赖redis、kafka

缓存

利用service worker来做静态资源的缓存,根据匹配url、正则,第一次获取将资源存在Cache Storage里,如果service-worker.js有更新,会重新获取最新的资源,并再次cache。支持离线缓存

tips:跨域缓存需要 workbox-cacheable-response 插件

缓存策略

  1. CacheFirst:缓存、回退到网络。适用于离线优先方式构建,对缓存中的资源提供缓存,对未在缓存中的资源提供网络请求并缓存下来
  2. CacheOnly:仅缓存,适用于静态资源
  3. NetworkFirst: 网络、回退到缓存。为在线用户提供最新内容,但离线用户会获得较旧的缓存版本。适用于频繁更新的资源
  4. NetworkOnly:仅网络请求,适用于没有相应离线资源的对象
  5. StaleWhileRevalidate:缓存、网络同时进行,适用于网络比硬盘读取更快。会造成流量浪费

缓存优先级

  1. Service Worker,可自行操作缓存策略
  2. Memory Cache
  3. Disk Cache(http缓存),server下发http头控制。响应内容的引用存入Memory Cahce
  4. 网络请求

效果

image

代码示例

workbox.routing.registerRoute(
    /.*\.(?:|png|gif|jpg|jpeg|svg|mp4|js|css|image)$/,
    new workbox.strategies.CacheFirst({
        plugins: [
            new workbox.cacheableResponse.CacheableResponsePlugin({
                statuses: [0, 200]
            })
        ]
    })
);

小结

  1. service worker缓存应用场景:离线缓存。但感觉静态资源有cdn的支持,走memory cache、disk cache足够了。
  2. HTTP缓存空间有限,容易被冲掉。多一层缓存,命中的概率更高了。

离线可用

监听fetch事件,如果是路由请求,则返回离线html

self.addEventListener('fetch', (evt) => {
  if (evt.request.mode !== 'navigate') {
    return;
  }
  evt.respondWith(
      fetch(evt.request)
          .catch(() => {
            return caches.open(CACHE_NAME)
                .then((cache) => {
                  return cache.match('offline.html');
                });
          })
  );
});

小结

可以用,但没啥必要

整体总结

  1. web app安装可取,可提升体验。其它不是很需要。