PWA-爱而不得

252 阅读4分钟

背景

以前用RN做过一个App,因时间成本太高搁置了,后来趁休假做了H5版的还在吗挂上去。 PWA火的时候大概了解了下,后来想在公司业务上用也没上成,所以就周未宅家支持了pwa看能不能提升下体验。

PWA

PWA(Progressive Web Apps) 渐进式web应用,Google推出的致力于将网页app化的项目。弥补web app对比native app急缺的几个能力,比如离线使用、后台加载、添加到主屏和消息推送等,同时具备“无需安装、用完即走”的特性。

曾经火了一阵,后来就没声音了,不是技术本身不好,是环境不适合,孤岛理论,圈地收割。

参考:

Web App Manifest

配置文件(建议命名为pwa.webmanifest),目的是将Web应用程序安装到设备的主屏幕。

{
    "name": "Minimal app to try PWA",
    "short_name": "AppName",
    "display": "standalone",
    "start_url": "/",
    "theme_color": "#8888ff",
    "background_color": "#aaaaff",
    "icons": [
        {
          "src": "icon.png",
          "sizes": "256x256",
          "type": "image/png"
        }
    ]
}

详细配置参考:developer.mozilla.org/zh-CN/docs/…

Service Worker

独立于UI主线程的线程避免阻塞渲染,用于处理耗费性能的事情,可以实现:后台消息传递、网络代理,转发请求,伪造响应、离线缓存、消息推送、…

特性

  • 须支持https(开发用localhost)
  • sw并不会一直在运行状态,生命周期与它所处理的事件执行联系在一起(作用域下事件触发,被唤醒处理事件,然后终止)

生命周期

  • installing,注册时会下载js、解析进入安装状态。成功进入installed或失败进入redundant。可监听install事件中使用waitUntil实现延期。
  • installed/waiting,另一个激活的worker在控制应用会维持在waiting状态。
  • activating,激活前会触发activate事件
  • activated,激活后开始监听功能性事件(fetch、push),serviceworker只能够在页面开始加载前控制页面
  • redundant,serviceworker独立于浏览器窗口或标签页,打开新标签浏时览器检测到试图注册的sw已激活就不会再次安装。安装激活事件只会运行一次。

事件

  • fetch,页面发起http请求时sw可以通过fetch事件拦截请求,并且给出自己的响应。
  • message,页面和sw之间可通过posetMessage()发消息,通过message事件接收。可将sw作为中间纽带,使得一个域名或子域名下的多页面通信。

注册

  • 默认scope是sw文件所在目录,只能指定scope为所在目录或子目录域。
  • 如果sw文件的返回头中指定了service-worker-allowed:/,则可以设置任意根目录下的scope。
  • 同域下可注册多个不同scope的sw。 参考:谨慎处理 Service Worker 的更新

平台差异

各平台浏览器实现差异...

部分Code

this.addEventListener('load,function(event){
    if (navigator.serviceWorker) {
        navigator.serviceWorker.register('static/service-worker.js',{scope:'/static/'})
            .then(registration=> {
                console.log('service worker 注册成功');
            }).catch(function (err) {
                console.log('servcie worker 注册失败')
            });

        navigator.serviceWorker.oncontrollerchange = function(){
            alert('页面已经更新');
        }
        if(!navigator.online){
            alert('当前离线,为您展示缓存内容');
            window.addEventListener('online',()=>{
                alert('网络已连接,请刷新获取最新内容');
            })
        }
    }
})
let CACHE_NAME='cache_v0';
let CACHE_URLS=[
    '/',
    '/js/main.js',
    '/js/render.js',
    'css/main.css',
    'img/log.png'
];
this.addEventListener('install,function(event){
    event.waitUntil(
        caches.open(CACHE_NAME).then(function(cache){
            cache.addAll(CACHE_URLS);
        }).then(()=>{
            self.skipWaiting();
        })
    );
})
this.addEventListener('activate',function(event){
    event.waitUntil(
        Promise.all([
            this.clients.claim(); //不刷新接管已加载页面
            caches.keys().then(cacheList=>{
                return Promise.all(
                    cacheList.map(cacheName=>{
                        if(cacheName != CACHE_NAME){
                            return caches.delete(cacheName);
                        }
                    })
                )
            })
        ])
    );
})
this.addEventListener('fetch',function(event){
    let url = new URL(event.request.url);
    if(url.origin != self.origin){  //外域cdn资源
        return;
    }
    if(event.request.url.include('api/')){
        event.responseWith(
            fetch(event.request).then(
                cache.open(CACHE_NAME).then(
                    cache=>cache.put(req,res.clone())
                )
                return res;
            ).catch(
                return catchs.match(event.request);
            )
        )
        return;
    }
    event.responseWith(
        fetch(vent.request).catch(()=>{
            return catchs.match(event.request);
        })
    );
})

使用了PWA的网站

实践上线

调试

  • 控制台-application-Manifest/service worker
  • chrome://inspect/#service-workers 可以查看到当前运行的service worker

workbox

找到现成的方案,workbox

webpack插件:workbox-webpack-plugin

  • GenerateSW,简单快速使用,自动生成sw文件,自行手动注册即可
  • InjectManifest,适合于细粒度控制,可以结合已有代码使用

缓存策略,

  • NetworkFirst
  • CacheFirst
  • NetworkOnly
  • CacheOnly
  • StaleWhileRevalidate从缓存中读取同时发送请求更新缓存

总结

  • 如果能添加到桌面,一等公民,体验上也会较普通浏览器中有较大提升
  • Android上,Chrome无疑是支持最好的,前提是用户手机能安装Chrome,且Chrome有桌面快捷方式的权限(正常人都找不到开启权限的地方),国内厂商自带浏览器个别支持(huawei、mi),主流浏览器(uc、qq、360)完全不支持
  • iOS虽然晚支持,不让其它浏览器添加桌面,Safari也不会弹提示,但能Safari上能判断并做出引导,添加桌面后图标上也无特殊标识,所以反而是目前可做到效果最好的 所以总结下来就是->爱而不得