yizihan / blog

Record
1 stars 0 forks source link

PWA - 渐进式网络应用 #12

Open yizihan opened 6 years ago

yizihan commented 6 years ago

PWA

Progressive Web App 渐进式网络应用

为什么是渐进式

Service Worker

用JS编写的文件,能够代理请求,并且能够操作浏览器缓存,通过将缓存的内容直接返回,让请求能够瞬间完成。

浏览器中的JavaScript都是运行在一个单一主线程上的,在同一时间内只能做一件事情。

Service Worker功能和特性:

使用Service Worker

技术依赖:

注册 register

在JS主线程(常规的页面的js)注册Service Worker

if('serviceWorker' in navigator) {
    // 确保页面加载完毕
    window.addEventListener('load', () => {
        // 注册位于/sw.js的Service Worker
        navigator.serviceWorker.register('/sw.js', {scope: '/'})
            // 注册完成
            .then((registration) => {
                console.log('ServiceWorker registration successful with scope: ', registration.scrope); // 整个网站的控制权
            })
            .catch((err) => {
                console.log('ServiceWorker registration failed: ', err);
            })
    })
}

安装 install

在Service Worker安装成功后,install事件被触发。 install事件一般是被用来填充浏览器的离线缓存能力。 当安装成功之后,Service Worker就会激活。

// sw.js
this.addEventListener('install', (event) => {
    event.waitUntil(
        // caches.open 创建一个版本号为v1的缓存 返回promise
        caches.open('v1')
            .then((cache) => {  // cache == 版本号为v1的缓存
                // 在创建的缓存上调用addAll(),添加需要缓存的资源路径
                return cache.addAll([
                    '/',
                    '/index.html',
                    '/main.css',
                    '/main.js',
                    '/image.jpeg'
                ]);
            })
    )
});

自定义请求响应

我们可以在install的时候进行静态资源缓存,也可以通过fetch事件处理回调来代理页面请求从而实现动态资源缓存。

this.addEventListener('fetch', (event) => {
    event.respondWith(
        caches.match(event.request)
            .then((response) => {
                if(response) return response;
                // 克隆原始请求
                var request = event.request.clone();
                return fetch(request).then((httpRes) => {
                    // 请求失败时,直接返回失败结果
                    if(!httpRes || httpRes.status !== 200) {
                        return httpRes;
                    }
                    // 请求成功时,将请求结果缓存起来
                    var responseClone = httpRes.clone();
                    caches.open('v1').then((cache) => {
                        cache.put(event.request, responseClone);
                    });
                    return httpRes;
                })
            })
    )
})

install与fetch比较

自动更新所有页面

如果希望在有了新版本时,所有的页面都得到及时自动更新怎么办? 可以在install事件中执行 self.skipWaiting()方法跳过waiting状态,然后直接进入activate阶段。 然后监听activate事件,通过 self.clients.claim()方法,更新所有客户端上的Service Worker。

self.addEventListener('install', (event) => {
    // skipWaiting()强制当前处在waiting状态的Service Worker进入activate状态
    event.waitUntil(self.skipWaiting())
});

self.addEventListener('activate', (event) => {
    event.waitUntil(
        Promise.all([
            // 更新客户端
            self.clients.claim();   // ???
            caches.keys().then((cacheList) => {
                return Promise.all(
                    // 清理旧版本
                    cacheList.map((cacheName) => {
                        if(cacheName !== 'v1') {
                            return caches.delete(cacheName);
                        }
                    })
                )
            }) 
        ])
    )
})

手动更新Service Worker

手动借助 Registration.update() 更新

var version = '1.0.1';
navigator.serviceWorker.register('/sw.js').then((reg) => {
    if(localStorage.getItem('sw_version') !== version) {
        reg.update().then(() => {
            localStorage.setItem('sw_version', version);
        })
    }
})

Service Worker生命周期

Service Worker的使用过程很简单,所处理的事情也相对单一,我们基本上需要做的就是利用这个API做好站点的缓存策略。 在页面脚本中注册Service Worker文件所在的URL,Worker就可以开始激活了,激活后的Service Worker可以监听当前域下的功能性事件,比如资源请求(fetch)、推送通知(push)、后台同步(sync)。 在这一系列的流程中,从Service Worker的注册到消失,经历了生命周期中的不同状态。

生命周期步骤:

  1. serviceWorker.register()注册Service Worker,在注册过程中,浏览器会在后台尝试Service Worker的安装步骤。
  2. 注册成功,Service Worker在ServiceWorkerGlobalScope环境中运行;这是一个特殊的worker context,与主脚本的运行线程相独立,同时没有访问DOM的能力。
  3. 后台开始安装,在安装的过程中需要缓存一些静态资源。
  4. 激活Service Worker,必须在Service Worker安装成功之后,才能开始激活步骤;当安装完成后,会接收到一个激活事件(activate event)。激活事件的处理函数中,主要操作是清理旧版本的Service Worker脚本中缓存的资源。
  5. 激活成功后Service Worker可以控制页面了,但是只针对在成功注册了Service Worker后打开的页面。也就是说,页面打开时有没有Service Worker决定了接下来页面的生命周期内受不受Service Worker控制。所以,只有当页面刷新后,之前不受Service Worker控制的页面才有可能被控制起来。

生命周期

支持的事件

事件

参考文章: LAVAS 文档