来自 澳门威尼斯人平台 2019-12-29 05:22 的文章
当前位置: 澳门威尼斯人平台 > 澳门威尼斯人平台 > 正文

渐进式网页应用PWA

使用 Service Worker 做一个 PWA 离线网页应用

2017/10/09 · JavaScript · PWA, Service Worker

原文出处: 人人网FED博客   

在上一篇《我是怎样让网站用上HTML5 Manifest》介绍了怎么用Manifest做一个离线网页应用,结果被广大网友吐槽说这个东西已经被deprecated,移出web标准了,现在被Service Worker替代了,不管怎么样,Manifest的一些思想还是可以借用的。笔者又将网站升级到了Service Worker,如果是用Chrome等浏览器就用Service Worker做离线缓存,如果是Safari浏览器就还是用Manifest,读者可以打开这个网站感受一下,断网也是能正常打开。

参考资料
MDN --- Service Worker API
Service Workers: an Introduction
服务工作线程生命周期
Service Worker Cookbook(收集了Service Worker的一些实践例子)
理解 Service Workers

你的公司会受益于渐进式网页应用吗?

渐进式网页应用(Progressive Web Apps,简称PWA)是一个新的概念,它弥合了网站(Website)和移动应用(Mobile App)之间的差异。它们能够确保离线功能的可用性,并且能够提升速度和性能”。

企业采用这项技术有诸多好处。事实上,渐进式网页应用使Google、AliExpress和FlipKart的留存率(retention rate)和转化率(conversion rate)提高了50%~100%。下面我们将了解到,渐进式网页应用通过结合两方面的优势,如何改进用户体验、加强访问者的参与度和提高转化率。

简书放不了demo,demo可以看原文

1. 什么是Service Worker

Service Worker是谷歌发起的实现PWA(Progressive Web App)的一个关键角色,PWA是为了解决传统Web APP的缺点:

(1)没有桌面入口

(2)无法离线使用

(3)没有Push推送

那Service Worker的具体表现是怎么样的呢?如下图所示:

图片 1

Service Worker是在后台启动的一条服务Worker线程,上图我开了两个标签页,所以显示了两个Client,但是不管开多少个页面都只有一个Worker在负责管理。这个Worker的工作是把一些资源缓存起来,然后拦截页面的请求,先看下缓存库里有没有,如果有的话就从缓存里取,响应200,反之没有的话就走正常的请求。具体来说,Service Worker结合Web App Manifest能完成以下工作(这也是PWA的检测标准):

图片 2

包括能够离线使用、断网时返回200、能提示用户把网站添加一个图标到桌面上等。

温馨提示

渐进式网页应用的优点:

离线模式

给人的感觉是应用,但运行机制是网站

提高性能

能在设备上快速安装

推送通知 (push notifications)

不必提交到应用软件商店(App Store)

离线模式

网站在某些情况下是有局限性的,在涉及到互联网连接的时候更是如此;没有网络连接时,网站即便能够显示出来,也不可能正常运行。另一方面,移动应用通常是自包含的(self-contained),这方便用户离线浏览,从而大大增加了用户参与度和软件可用性。

这是通过保存访问者已访问过的信息来实现的。这意味着任何时候,即使是没有连接网络的时候,访问者都可以访问渐进式网页应用已访问过的页面。

在没有网络连接的情况下,当用户浏览到先前未访问过的页面时,不是在浏览器中提示错误信息,而是可能显示一个定制的离线页面。该页面可能会显示品牌Logo和基本信息,有时甚至是更先进的功能,旨在吸引用户停留在该页面上。

很明显,这样做的好处在于增加了访客留在该网站上的可能性,而不是促使用户关闭浏览器,等有了网络连接再继续使用。

这已经成为移动应用大幅增长的主要原因之一,催生了一个达数十亿美元的行业。但现在渐进式网页应用正通过帮助普通网站为所有设备实现离线功能,慢慢蚕食这部分市场。

渐进式网页应用对于某些商业模式可能没有财务意义。例如,依赖诸如Google AdSense等服务的网站可能不会感兴趣,因为访问者无法在上面点击广告。但电子商务商店显而易见是渐进式网页应用可以发挥所长的平台。

因访问者在离线模式下也可以访问产品目录,这使得企业有大幅提高他们的客户留存率和参与度的可能。尤其在那些按数据使用量来支付网络费用的国家中,允许用户以离线模式浏览网页,可能对用户来说是个额外的刺激:对比其他商家,用户更可能选择有渐进式网页应用的商家。

给人的感觉是应用,但运行机制是网站

渐进式网页应用的主要卖点在于外观和体验通常会类似于移动应用,让用户在熟悉的环境下操作,同时仍然具有动态数据和数据库访问的全部网站功能。

虽然如何进行渐进式网页应用的设计和编程由每个开发人员自行决定,但是鉴于移动应用比网站更能提供优越的用户体验,因此大多数人还是会完全采用移动应用的现有框架和常规理论。

像网站一样,渐进式网页应用可以通过URL访问,因此可以通过搜索引擎进行索引。这意味着可以在搜索引擎,比如Google和Bing上找到该页面。与所有内部数据只能局限于内部访问的移动应用相比,这是一个巨大的优势。

根据项目要求,渐进式网页应用可以设计成与现有的企业网站或移动应用完全相同,也可以有意设计成有所不同以便让用户感知他们正在浏览渐进式网页应用。甚至可以将渐进式网页应用无缝地集成到现有的网站/应用程序的结构和设计中。

感谢2016年7月的Google研究。

在Google进行的同一项研究中,我们发现所有网站访问者中有11.5%接受并下载了相应的渐进式网页应用。这对任何类型的网站来说都是很高的转化率,并且超过大多数电子邮件新闻注册和电子商务购买的转化率。

结合上述统计数据和接受推送通知的用户数量,我们最终确定转化率在6-7%左右,这对现有网站流量来说仍然是一个可喜的数字。

提高性能

渐进式网页应用的速度要明显快得多,这要归功于底层技术能够缓存和提供文本、样式表、图片以及Web站点上的其他内容。

这得益于服务工作者(service worker),它们的运行独立于Web站点,只请求原始数据,而不涉及任何样式或布局信息。

显然,速度的提升可以改善用户体验和提高留存率。同时,很多报告显示优化性能也能大大提高转化率,这可从销售角度来说增加了渐进式网页应用的价值。

感谢2016年7月的Google研究。(controlled指的是由服务工作者控制页面;supported指的是浏览器支持服务工作者,但是服务工作者没有控制页面。)

上面的图表显示了安装服务工作者,并控制页面内容加载之后,能够明显缩短加载时间。

第一个表格显示的是桌面用户的加载时间。用户使用服务工作者加载网页的时间与使用浏览器加载缓存内容的时间相比减少了29%。

对移动设备而言,性能仍然有明显提高,虽然不及桌面应用,但加载时间还是减少了22%。

值得注意的是,在两种测试中的第三行都基于首次访问的数据,因此无论是否安装服务工作者,结果是一样的 。这是因为服务工作者只有在二次访问时才起作用。

Service Worker 是什么?

service worker 是独立于当前页面的一段运行在浏览器后台进程里的脚本。
service worker不需要用户打开 web 页面,也不需要其他交互,异步地运行在一个完全独立的上下文环境,不会对主线程造成阻塞。基于service worker可以实现消息推送,静默更新以及地理围栏等服务。
service worker提供一种渐进增强的特性,使用特性检测来渐渐增强,不会在老旧的不支持 service workers 的浏览器中产生影响。可以通过service workers解决让应用程序能够离线工作,让存储数据在离线时使用的问题。

注意事项:
1.service worker运行在它们自己的完全独立异步的全局上下文中,也就是说它们有自己的容器。
2.service worker没有直接操作DOM的权限,但是可以通过postMessage方法来与Web页面通信,让页面操作DOM。
3.service worker是一个可编程的网络代理,允许开发者控制页面上处理的网络请求。
4.浏览器可能随时回收service worker,在不被使用的时候,它会自己终止,而当它再次被用到的时候,会被重新激活。
5.service worker的生命周期是由事件驱动的而不是通过Client。

2. Service Worker的支持情况

Service Worker目前只有Chrome/Firfox/Opera支持:

图片 3

Safari和Edge也在准备支持Service Worker,由于Service Worker是谷歌主导的一项标准,对于生态比较封闭的Safari来说也是迫于形势开始准备支持了,在Safari TP版本,可以看到:

图片 4

在实验功能(Experimental Features)里已经有Service Worker的菜单项了,只是即使打开也是不能用,会提示你还没有实现:

图片 5

但不管如何,至少说明Safari已经准备支持Service Worker了。另外还可以看到在今年2017年9月发布的Safari 11.0.1版本已经支持WebRTC了,所以Safari还是一个上进的孩子。

Edge也准备支持,所以Service Worker的前景十分光明。

  1. 使用限制
    Service Worker由于权限很高,只支持https协议或者localhost。
    个人认为Github Pages是一个很理想的练习场所。
  2. 储备知识
    Service Worker大量使用Promise,不了解的请移步:Javascript:Promise对象基础

能在设备上快速安装

另外很有意思的一点是在于,当用户访问网站时,一些浏览器会自动提示用户安装渐进式网页应用。这是通过浏览器自身所实现的唤起行动(call to action)来实现的。这使得渐进式网页应用更可信,同时增值了它的权威性和可靠性。

与移动应用相比,用户安装渐进式网页应用时无需很长的下载时间。同时,用户不会被转到Google Play或App Store,而是直接将应用程序下载到他们的设备上。

这意味着渐进式网页应用就像移动应用一样,在手机和平板电脑上有自己的图标,但无需经历乏味和缓慢的应用商店提交过程。

Service Worker生命周期

service worker拥有一个完全独立于Web页面的生命周期

图片 6

sw-lifecycle.png

  1. 注册service worker,在网页上生效
  2. 安装成功,激活 或者 安装失败(下次加载会尝试重新安装)
  3. 激活后,在sw的作用域下作用所有的页面,首次控制sw不会生效,下次加载页面才会生效。
  4. sw作用页面后,处理fetch(网络请求)和message(页面消息)事件 或者 被终止(节省内存)。

3. 使用Service Worker

Service Worker的使用套路是先注册一个Worker,然后后台就会启动一条线程,可以在这条线程启动的时候去加载一些资源缓存起来,然后监听fetch事件,在这个事件里拦截页面的请求,先看下缓存里有没有,如果有直接返回,否则正常加载。或者是一开始不缓存,每个资源请求后再拷贝一份缓存起来,然后下一次请求的时候缓存里就有了。

兼容性

推送通知

渐进式网页应用可选择实现各种设备特定的硬件功能,例如推送通知。软件发布商和开发人员可以完全控制如何实现这个功能,从而为通知新内容提供创新的解决方案。

对于电子商务网站,这可能意味着一个全新的销售入口渠道,因为直接显示在手机上的推送通知的读取次数要远远超过电子邮件形式的新闻信札以及社交媒体上的状态更新等。

此外,安装了渐进式网页应用的用户还可以在其主屏幕上看到图标,这会在用户每次使用手机时提醒他品牌名称和产品。这不仅仅是另一种销售策略,还可带来宝贵的品牌意识。但是如果用户安装许多应用程序和渐进式网页应用,通过推送通知发布最新产品、博客帖子(blog posts)、文章或其他相关信息, 可能会导致用户的通知区域杂乱无章。

感谢2016年7月的Google研究。

在所有下载渐进式网页应用的用户中,将近60%都授予渐进式网页应用发布推送通知的权限, 不过还有36.3%的用户没有点开推送通知,或者由于渐进式网页应用的个人设置没有收到推送通知。

将此数字与有多少网站访问者从主页上下载渐进式网页应用的统计数据结合起来,我们可以估计大约6-7%的网站现有流量能够转换为接受推送通知的渐进式网页应用用户。

Service Worker支持使用

(1)注册一个Service Worker

Service Worker对象是在window.navigator里面,如下代码:

JavaScript

window.addEventListener("load", function() { console.log("Will the service worker register?"); navigator.serviceWorker.register('/sw-3.js') .then(function(reg){ console.log("Yes, it did."); }).catch(function(err) { console.log("No it didn't. This happened: ", err) }); });

1
2
3
4
5
6
7
8
9
window.addEventListener("load", function() {
    console.log("Will the service worker register?");
    navigator.serviceWorker.register('/sw-3.js')
    .then(function(reg){
        console.log("Yes, it did.");
    }).catch(function(err) {
        console.log("No it didn't. This happened: ", err)
    });
});

在页面load完之后注册,注册的时候传一个js文件给它,这个js文件就是Service Worker的运行环境,如果不能成功注册的话就会抛异常,如Safari TP虽然有这个对象,但是会抛异常无法使用,就可以在catch里面处理。这里有个问题是为什么需要在load事件启动呢?因为你要额外启动一个线程,启动之后你可能还会让它去加载资源,这些都是需要占用CPU和带宽的,我们应该保证页面能正常加载完,然后再启动我们的后台线程,不能与正常的页面加载产生竞争,这个在低端移动设备意义比较大。

还有一点需要注意的是Service Worker和Cookie一样是有Path路径的概念的,如果你设定一个cookie假设叫time的path=/page/A,在/page/B这个页面是不能够获取到这个cookie的,如果设置cookie的path为根目录/,则所有页面都能获取到。类似地,如果注册的时候使用的js路径为/page/sw.js,那么这个Service Worker只能管理/page路径下的页面和资源,而不能够处理/api路径下的,所以一般把Service Worker注册到顶级目录,如上面代码的”/sw-3.js”,这样这个Service Worker就能接管页面的所有资源了。

图片 7

不必提交应用软件商店

随着需遵守的监管点不断增加,在Google Play、Windows Phone Apps或Apple App Store发布应用程序可能是一个乏味和耗时的过程。

通过使用渐进式网页应用,开发人员无需等待批准就可以推送新的更新,并且能在传统移动应用目前无法实现的级别上进行定期更新。

用户重新运行渐进式网页应用时,系统会自动下载更新。并且,可以通过推送通知,让用户获知应用更新已下载。而且,这同样不是强制性的,软件发布商可以完全控制将什么内容和信息推送给用户。

浏览器支持

service worker support

图片 8

navigator-serviceworker.png

(2)Service Worker安装和激活

注册完之后,Service Worker就会进行安装,这个时候会触发install事件,在install事件里面可以缓存一些资源,如下sw-3.js:

JavaScript

const CACHE_NAME = "fed-cache"; this.addEventListener("install", function(event) { this.skipWaiting(); console.log("install service worker"); // 创建和打开一个缓存库 caches.open(CACHE_NAME); // 首页 let cacheResources = ["]; event.waitUntil( // 请求资源并添加到缓存里面去 caches.open(CACHE_NAME).then(cache => { cache.addAll(cacheResources); }) ); });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const CACHE_NAME = "fed-cache";
this.addEventListener("install", function(event) {
    this.skipWaiting();
    console.log("install service worker");
    // 创建和打开一个缓存库
    caches.open(CACHE_NAME);
    // 首页
    let cacheResources = ["https://fed.renren.com/?launcher=true"];
    event.waitUntil(
        // 请求资源并添加到缓存里面去
        caches.open(CACHE_NAME).then(cache => {
            cache.addAll(cacheResources);
        })
    );
});

通过上面的操作,创建和添加了一个缓存库叫fed-cache,如下Chrome控制台所示:

图片 9

Service Worker的API基本上都是返回Promise对象避免堵塞,所以要用Promise的写法。上面在安装Service Worker的时候就把首页的请求给缓存起来了。在Service Worker的运行环境里面它有一个caches的全局对象,这个是缓存的入口,还有一个常用的clients的全局对象,一个client对应一个标签页。

在Service Worker里面可以使用fetch等API,它和DOM是隔离的,没有windows/document对象,无法直接操作DOM,无法直接和页面交互,在Service Worker里面无法得知当前页面打开了、当前页面的url是什么,因为一个Service Worker管理当前打开的几个标签页,可以通过clients知道所有页面的url。还有可以通过postMessage的方式和主页面互相传递消息和数据,进而做些控制。

install完之后,就会触发Service Worker的active事件:

JavaScript

this.addEventListener("active", function(event) { console.log("service worker is active"); });

1
2
3
this.addEventListener("active", function(event) {
    console.log("service worker is active");
});

Service Worker激活之后就能够监听fetch事件了,我们希望每获取一个资源就把它缓存起来,就不用像上一篇提到的Manifest需要先生成一个列表。

你可能会问,当我刷新页面的时候不是又重新注册安装和激活了一个Service Worker?虽然又调了一次注册,但并不会重新注册,它发现”sw-3.js”这个已经注册了,就不会再注册了,进而不会触发install和active事件,因为当前Service Worker已经是active状态了。当需要更新Service Worker时,如变成”sw-4.js”,或者改变sw-3.js的文本内容,就会重新注册,新的Service Worker会先install然后进入waiting状态,等到重启浏览器时,老的Service Worker就会被替换掉,新的Service Worker进入active状态,如果不想等到重新启动浏览器可以像上面一样在install里面调skipWaiting:

JavaScript

this.skipWaiting();

1
this.skipWaiting();

Service Worker的兼容性

面临的困难

缺乏通用支持

以下有些重要信息需要注意,主要是并非所有浏览器都支持渐进式网页应用。

Google Chrome和Opera这两个浏览器对服务工作者和渐进式网页应用给与了极大的支持。

苹果的Safari浏览器目前仍然不提供渐进式网页应用支持,虽然有消息说他们会考虑,但迄今为止没有任何具体的内容发布。

微软表示他们将在2016年7月之前在Edge上实施渐进式网页应用,但目前仍然没有关于这方面的消息。

然而,即使不是所有的浏览器都支持渐进式网页应用,对不兼容浏览器的用户也不会造成任何问题,因为这些浏览器只是忽略了渐进式网页应用,依然能够像往常一样显示网站。

polyfill

使用ServiceWorker cache polyfill让旧版本浏览器支持 ServiceWorker cache API,

(3)fetch资源后cache起来

如下代码,监听fetch事件做些处理:

JavaScript

this.addEventListener("fetch", function(event) { event.respondWith( caches.match(event.request).then(response => { // cache hit if (response) { return response; } return util.fetchPut(event.request.clone()); }) ); });

1
2
3
4
5
6
7
8
9
10
11
12
this.addEventListener("fetch", function(event) {
    event.respondWith(
        caches.match(event.request).then(response => {
            // cache hit
            if (response) {
                return response;
            }
            return util.fetchPut(event.request.clone());
        })
    );
});

先调caches.match看一下缓存里面是否有了,如果有直接返回缓存里的response,否则的话正常请求资源并把它放到cache里面。放在缓存里资源的key值是Request对象,在match的时候,需要请求的url和header都一致才是相同的资源,可以设定第二个参数ignoreVary:

JavaScript

caches.match(event.request, {ignoreVary: true})

1
caches.match(event.request, {ignoreVary: true})

表示只要请求url相同就认为是同一个资源。

上面代码的util.fetchPut是这样实现的:

JavaScript

let util = { fetchPut: function (request, callback) { return fetch(request).then(response => { // 跨域的资源直接return if (!response || response.status !== 200 || response.type !== "basic") { return response; } util.putCache(request, response.clone()); typeof callback === "function" && callback(); return response; }); }, putCache: function (request, resource) { // 后台不要缓存,preview链接也不要缓存 if (request.method === "GET" && request.url.indexOf("wp-admin") < 0 && request.url.indexOf("preview_id") < 0) { caches.open(CACHE_NAME).then(cache => { cache.put(request, resource); }); } } };

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
let util = {
    fetchPut: function (request, callback) {
        return fetch(request).then(response => {
            // 跨域的资源直接return
            if (!response || response.status !== 200 || response.type !== "basic") {
                return response;
            }
            util.putCache(request, response.clone());
            typeof callback === "function" && callback();
            return response;
        });
    },
    putCache: function (request, resource) {
        // 后台不要缓存,preview链接也不要缓存
        if (request.method === "GET" && request.url.indexOf("wp-admin") < 0
              && request.url.indexOf("preview_id") < 0) {
            caches.open(CACHE_NAME).then(cache => {
                cache.put(request, resource);
            });
        }
    }
};

需要注意的是跨域的资源不能缓存,response.status会返回0,如果跨域的资源支持CORS,那么可以把request的mod改成cors。如果请求失败了,如404或者是超时之类的,那么也直接返回response让主页面处理,否则的话说明加载成功,把这个response克隆一个放到cache里面,然后再返回response给主页面线程。注意能放缓存里的资源一般只能是GET,通过POST获取的是不能缓存的,所以要做个判断(当然你也可以手动把request对象的method改成get),还有把一些个人不希望缓存的资源也做个判断。

这样一旦用户打开过一次页面,Service Worker就安装好了,他刷新页面或者打开第二个页面的时候就能够把请求的资源一一做缓存,包括图片、CSS、JS等,只要缓存里有了不管用户在线或者离线都能够正常访问。这样我们自然会有一个问题,这个缓存空间到底有多大?上一篇我们提到Manifest也算是本地存储,PC端的Chrome是5Mb,其实这个说法在新版本的Chrome已经不准确了,在Chrome 61版本可以看到本地存储的空间和使用情况:

图片 10

其中Cache Storage是指Service Worker和Manifest占用的空间大小和,上图可以看到总的空间大小是20GB,几乎是unlimited,所以基本上不用担心缓存会不够用。

一、 生命周期

个人觉得先理解一下它的生命周期很重要!之前查资料的时候,很多文章一上来就监听install事件、waiting事件、activate事件……反正我是一脸懵逼。

图片 11

Service Worker的生命周期

不列在应用商店目录中

有些人可能会认为自己的渐进式网页应用没有列在应用商店中会降低曝光率,但通常情况并非如此。

事实上,与移动应用相比,渐进式网页应用可以通过Google或其他搜索引擎上搜索到,这与网站类似,而与移动应用有所不同。这意味着数十亿的日常搜索可能最终导致搜索到渐进式网页应用。

https

Server需要支持https

通过service worker可以劫持连接,伪造和过滤响应,为了避免这些问题,只能在HTTPS的网页上注册service workers,防止加载service worker的时候不被坏人篡改。

Github Pages是HTTPS的,可以通过Github做一些尝试

本文由澳门威尼斯人平台发布于澳门威尼斯人平台,转载请注明出处:渐进式网页应用PWA

关键词: