PWA (渐进式 Web App)

渐进式Web应用(PWA)入门教程(下)

2018/05/25 · 基础技术 ·
PWA

原文出处: Craig
Buckler   译文出处:葡萄城控件   

上篇文章我们对渐进式Web应用(PWA)做了一些基本的介绍。

渐进式Web应用(PWA)入门教程(上)

在这一节中,我们将介绍PWA的原理是什么,它是如何开始工作的。

渐进式Web应用(PWA)入门教程(上)

2018/05/23 · 基础技术 ·
PWA

原文出处: Craig
Buckler   译文出处:葡萄城控件   

最近关于渐进式Web应用有好多讨论,有一些人还在质疑渐进式Web应用是否就是移动端未来。

但在这篇文章中我并不会将渐进式APP和原生的APP进行比较,但有一点是可以肯定的,这两种APP的目标都是使用户体验变得更好。

移动端Web应用有很多优秀的概念让人应接不暇,但好在编写一个渐进式Web应用不是一个很困难的事情。在这篇文章里将向你介绍如何把一个普通的网站转换成渐进式Web应用。你可以按照这篇文章一步一步地做,做完之后你的网站将可以实现离线访问,并且可以在桌面上创建该网站的图标。那么下面即将开始入门教程。

【 PWA 】

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

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

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

PWA之所以这么火,还有一个原因是因为它里面含有多项核心技术,比如:Service
Worker、Fetch、Caching Files、Web Push Notifications等等。

第一步:使用HTTPS

渐进式Web应用程序需要使用HTTPS连接。虽然使用HTTPS会让您服务器的开销变多,但使用HTTPS可以让您的网站变得更安全,HTTPS网站在Google上的排名也会更靠前。

由于Chrome浏览器会默认将localhost以及127.x.x.x地址视为测试地址,所以在本示例中您并不需要开启HTTPS。另外,出于调试目的,您可以在启动Chrome浏览器的时候使用以下参数来关闭其对网站HTTPS的检查:

  • –user-data-dir
  • –unsafety-treat-insecure-origin-as-secure

什么是渐进式Web应用?

渐进式Web应用是一种全新的Web技术,让Web应用和原生APP的体验相近或一致。

渐进式Web应用它可以横跨Web技术及Native
APP开发的解决方案,对于开发者的优势如下:

  1. 你只需要关心W3C的Web标准,不用关心各种Native APP的代码。
  2. 用户可以在安装应用之前先试用。
  3. 在渐进式Web应用中,你不需要使用各种应用商店来分发应用,也不用关心应用发布时奇怪的审核标准以及应用内购的平台抽成。另外,应用程序更新是自动进行的,无需用户交互,所以整体的使用体验对于用户来讲更为的平滑。
  4. 渐进式Web应用的“安装”过程很快,只需要在主屏幕上添加一个图标即可。
  5. 渐进式Web应用启动时可以显示一个好看的启动画面。
  6. 你可以在渐进式Web应用中提供具有全屏体验的应用。
  7. 通过系统通知等形式提高用户的粘性。
  8. 渐进式Web应用将会在本地缓存必要的文件,所以渐进式Web应用会比普通的Web应用的性能更好。
  9. 轻量级安装——你只需要缓存几百KB的数据即可。
  10. 所有的数据传输必须使用安全的HTTPS连接
  11. 渐进式Web应用可以离线缓存数据,并且会在重新连接互联网时重新同步数据。

        今天开始 Research 一个新的前端技术,PWA( 全称:Progressive Web
App )也就是说这是个渐进式的网页应用程序。这个技术的呢是 Google
公司于2015 年提出的,2016 年 6
月才推广的项目。针对这一项技术目前在国外似乎已经很流行了,目前应用这项技术最火热的应用是在印度(
大家或许会疑惑为什么是在印度最流行呢吧,下文中会告诉大家哦 ),既然 PWA
这项技术在国外已经非常的流行了,那么在国内或许会不远了( Angular 5
中新增的功能中,重点在于能够更轻松的构建渐进式网络应用程序,也就是 PWA
了。作为Google 和 mozilla 的产物,肯定会越来越重视啦
)我引用一下关于PWA技术的一篇最早的博客文中的一句话吧: “ escaping tabs
without losing our soul “( 翻译一下哈:逃避选项卡而不丢失我们的灵魂
)。

渐进式网页应用的优点:

离线模式

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

提高性能

能在设备上快速安装

推送通知 (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%。

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

1、服务工作线程(Service Worker)

第二步:创建一个应用程序清单(Manifest)

应用程序清单提供了和当前渐进式Web应用的相关信息,如:

  • 应用程序名
  • 描述
  • 所有图片(包括主屏幕图标,启动屏幕页面和用的图片或者网页上用的图片)

本质上讲,程序清单是页面上用到的图标和主题等资源的元数据。

程序清单是一个位于您应用根目录的JSON文件。该JSON文件返回时必须添加Content-Type: application/manifest+json 或者 Content-Type: application/jsonHTTP头信息。程序清单的文件名不限,在本文的示例代码中为manifest.json

{ “name” : “PWA Website”, “short_name” : “PWA”, “description” : “An
example PWA website”, “start_url” : “/”, “display” : “standalone”,
“orientation” : “any”, “background_color” : “#ACE”, “theme_color” :
“#ACE”, “icons”: [ { “src” : “/images/logo/logo072.png”, “sizes” :
“72×72”, “type” : “image/png” }, { “src” : “/images/logo/logo152.png”,
“sizes” : “152×152”, “type” : “image/png” }, { “src” :
“/images/logo/logo192.png”, “sizes” : “192×192”, “type” : “image/png” },
{ “src” : “/images/logo/logo256.png”, “sizes” : “256×256”, “type” :
“image/png” }, { “src” : “/images/logo/logo512.png”, “sizes” :
“512×512”, “type” : “image/png” } ] }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
{
  "name"              : "PWA Website",
  "short_name"        : "PWA",
  "description"       : "An example PWA website",
  "start_url"         : "/",
  "display"           : "standalone",
  "orientation"       : "any",
  "background_color"  : "#ACE",
  "theme_color"       : "#ACE",
  "icons": [
    {
      "src"           : "/images/logo/logo072.png",
      "sizes"         : "72×72",
      "type"          : "image/png"
    },
    {
      "src"           : "/images/logo/logo152.png",
      "sizes"         : "152×152",
      "type"          : "image/png"
    },
    {
      "src"           : "/images/logo/logo192.png",
      "sizes"         : "192×192",
      "type"          : "image/png"
    },
    {
      "src"           : "/images/logo/logo256.png",
      "sizes"         : "256×256",
      "type"          : "image/png"
    },
    {
      "src"           : "/images/logo/logo512.png",
      "sizes"         : "512×512",
      "type"          : "image/png"
    }
  ]
}

程序清单文件建立完之后,你需要在每个页面上引用该文件:

<link rel=”manifest” href=”/manifest.json”>

1
<link rel="manifest" href="/manifest.json">

以下属性在程序清单中经常使用,介绍说明如下:

  • name: 用户看到的应用名称
  • short_name: 应用短名称。当显示应用名称的地方不够时,将使用该名称。
  • description: 应用描述。
  • start_url: 应用起始路径,相对路径,默认为/。
  • scope: URL范围。比如:如果您将“/app/”设置为URL范围时,这个应用就会一直在这个目录中。
  • background_color: 欢迎页面的背景颜色和浏览器的背景颜色(可选)
  • theme_color: 应用的主题颜色,一般都会和背景颜色一样。这个设置决定了应用如何显示。
  • orientation: 优先旋转方向,可选的值有:any, natural, landscape,
    landscape-primary, landscape-secondary, portrait, portrait-primary,
    and portrait-secondary
  • display: 显示方式——fullscreen(无Chrome),standalone(和原生应用一样),minimal-ui(最小的一套UI控件集)或者browser(最古老的使用浏览器标签显示)
  • icons: 一个包含所有图片的数组。该数组中每个元素包含了图片的URL,大小和类型。

渐进式Web应用发展的现状

渐进式Web应用才刚刚开始发展,但实际上在国内,有些网站已经实际开始PWA的实践了,例如:微博、豆瓣、淘宝等平台。可能这时候聪明的你可能就会产生疑问,那这个PWA不就是和微信小程序一样吗,对是这样,二者的目的是一致的,就是在移动端为用户提供足够轻量且与原生应用使用体验相近的“轻”应用。

但就目前来讲,PWA是Google主推的一项技术标准,FireFox,Chrome以及一些基于Blink的浏览器已经支持渐进式Web应用了,Edge上对渐进式Web应用的支持还在开发。Apple公司也表示会考虑在自己Safari支持PWA。然而这项功能已经进入了WebKit内核的五年计划中。长期来看,对浏览器兼容性的支持方面应该已经不算太大问题了。况且在现阶段,在不支持渐进式Web应用的浏览器中,你的应用也只是无法使用渐进式Web应用的离线功能而已,除此之外的功能均可以正常使用。

而在微信这边,凭借庞大的用户基数和体量能否与PWA分庭抗礼乃至笑到最后目前还不得而知。

官网上给出 PWA 的宣传是 : Reliable ( 可靠的 )、Fast( 快速的
)、Engaging( 可参与的 )(
官网:https://developers.google.com/web/progressive-web-apps/ps:需要翻墙哦
 )。简单的说一下这三个特性:

能在设备上快速安装

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

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

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

服务工作线程是浏览器在后台独立于网页运行的脚本,它打开了通向不需要网页或用户交互的功能的大门。现在,它们已包括如推送通知和后台同步等功能。
简单代码示例:

第三步:创建一个 Service Worker

Service Worker
是一个可编程的服务器代理,它可以拦截或者响应网络请求。Service Worker
是位于应用程序根目录的一个个的JavaScript文件。

您需要在页面对应的JavaScript文件中注册该ServiceWorker:

if (‘serviceWorker’ in navigator) { // register service worker
navigator.serviceWorker.register(‘/service-worker.js’); }

1
2
3
4
if (‘serviceWorker’ in navigator) {
  // register service worker
  navigator.serviceWorker.register(‘/service-worker.js’);
}

如果您不需要离线的相关功能,您可以只创建一个 /service-worker.js文件,这样用户就可以直接安装您的Web应用了!

Service
Worker这个概念可能比较难懂,它其实是一个工作在其他线程中的标准的Worker,它不可以访问页面上的DOM元素,没有页面上的API,但是可以拦截所有页面上的网络请求,包括页面导航,请求资源,Ajax请求。

上面就是使用全站HTTPS的主要原因了。假设您没有在您的网站中使用HTTPS,一个第三方的脚本就可以从其他的域名注入他自己的ServiceWorker,然后篡改所有的请求——这无疑是非常危险的。

Service Worker 会响应三个事件:install,activate和fetch。

示例代码

大多数教程都讲述的是如何在Chrome上从零开始制作一个类似原生界面的应用。然而在这篇教程中,我们并不打算做一个单页面应用程序,所以在这我们也不必了解诸如Material
Design等知识。那么下面我们就直接看示例吧。

你可以从GitHub中获取本教程对应的示例代码。

本示例中提供了一个有四个网页的网站,一个CSS文件和一个JavaScript文件。这个网站可以在所有的现代浏览器上正常工作(IE10+)。如果你的浏览器支持渐进式Web应用,用户可以在离线状态下将会直接访问缓存中的页面。

要想运行此示例,请确保你已经安装了Node.js。并请打开命令行,使用以下命令来运行该示例:

node ./server.js [port]

1
node ./server.js [port]

以上命令中,[port]是可选部分,默认为8888。使用 Ctrl + C 即可停止Web服务器。

打开基于Blink内核的浏览器(Opera,Vivaldi,Chrome),然后在地址栏中输入 或者 Cmd/Ctrl + Shift +
I)来查看控制台信息。

图片 1图片 2

查看首页,也可以在页面上点击一下,然后使用以下方法进入离线模式:

选中Network标签或者Application -> Service Workers
标签下的“离线”选项。重新访问之前访问过的网页,之前网页仍然会加载:

图片 3图片 4

                 Reliable :
为什么他是可靠的呢,当用户从手机主屏幕启动时,不用考虑网络的状态是如何,都可以立刻加载出
PWA。                                         

推送通知

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

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

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

感谢2016年7月的Google研究。

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

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

if ('serviceWorker' in navigator) {
  window.addEventListener('load', function() {
    navigator.serviceWorker.register('/sw.js').then(function(registration) {
      // Registration was successful
      console.log('ServiceWorker registration successful with scope: ', registration.scope);
    }).catch(function(err) {
      // registration failed :(
      console.log('ServiceWorker registration failed: ', err);
    });
  });
}

Install事件

该事件将在应用安装完成后触发。我们一般在这里使用Cache
API缓存一些必要的文件。

首先,我们需要提供如下配置

  1. 缓存名称(CACHE)以及版本(version)。应用可以有多个缓存存储,但是在使用时只会使用其中一个缓存存储。每当缓存存储有变化时,新的版本号将会指定到缓存存储中。新的缓存存储将会作为当前的缓存存储,之前的缓存存储将会被作废。
  2. 一个离线的页面地址(offlineURL):当用户访问了之前没有访问过的地址时,该页面将会显示。
  3. 一个包含了所有必须文件的数组,包括保障页面正常功能的CSS和JavaScript。在本示例中,我还添加了主页和logo。当有不同的URL指向同一个资源时,你也可以将这些URL分别写到这个数组中。offlineURL将会加入到这个数组中。
  4. 我们也可以将一些非必要的缓存文件(installFilesDesirable)。这些文件在安装过程中将会被下载,但如果下载失败,不会触发安装失败。

// 配置文件 const version = ‘1.0.0’, CACHE = version + ‘::PWAsite’,
offlineURL = ‘/offline/’, installFilesEssential = [ ‘/’,
‘/manifest.json’, ‘/css/styles.css’, ‘/js/main.js’,
‘/js/offlinepage.js’, ‘/images/logo/logo152.png’ ].concat(offlineURL),
installFilesDesirable = [ ‘/favicon.ico’, ‘/images/logo/logo016.png’,
‘/images/hero/power-pv.jpg’, ‘/images/hero/power-lo.jpg’,
‘/images/hero/power-hi.jpg’ ];

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 配置文件
const
  version = ‘1.0.0’,
  CACHE = version + ‘::PWAsite’,
  offlineURL = ‘/offline/’,
  installFilesEssential = [
    ‘/’,
    ‘/manifest.json’,
    ‘/css/styles.css’,
    ‘/js/main.js’,
    ‘/js/offlinepage.js’,
    ‘/images/logo/logo152.png’
  ].concat(offlineURL),
  installFilesDesirable = [
    ‘/favicon.ico’,
    ‘/images/logo/logo016.png’,
    ‘/images/hero/power-pv.jpg’,
    ‘/images/hero/power-lo.jpg’,
    ‘/images/hero/power-hi.jpg’
  ];

installStaticFiles() 方法使用基于Promise的方式使用Cache
API将文件存储到缓存中。

// 安装静态资源 function installStaticFiles() { return
caches.open(CACHE) .then(cache => { // 缓存可选文件
cache.addAll(installFilesDesirable); // 缓存必须文件 return
cache.addAll(installFilesEssential); }); }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 安装静态资源
function installStaticFiles() {
  return caches.open(CACHE)
    .then(cache => {
      // 缓存可选文件
      cache.addAll(installFilesDesirable);
      // 缓存必须文件
      return cache.addAll(installFilesEssential);
    });
}

最后,我们添加一个install的事件监听器。waitUntil方法保证了service
worker不会安装直到其相关的代码被执行。这里它会执行installStaticFiles()方法,然后self.skipWaiting()方法来激活service
worker:

// 应用安装 self.addEventListener(‘install’, event => {
console.log(‘service worker: install’); // 缓存主要文件 event.waitUntil(
installStaticFiles() .then(() => self.skipWaiting()) ); });

1
2
3
4
5
6
7
8
9
10
11
12
// 应用安装
self.addEventListener(‘install’, event => {
  console.log(‘service worker: install’);
  // 缓存主要文件
  event.waitUntil(
    installStaticFiles()
    .then(() => self.skipWaiting())
  );
});

连接移动端安装

除了在PC浏览器访问外,你也可以在移动设备上访问该示例。使用USB线缆将你的移动设备连接到电脑上,然后从右上角三个点菜单中打开More
tools – Remote devices标签

图片 5图片 6

点击左侧的Settings菜单,然后添加一条端口映射(Port Forwarding)的规则,将8888映射为localhost:8888,现在你可以直接在手机打开Chrome然后访问http://localhost:8888 。

你可以使用浏览器的“添加到主屏幕”功能将当前网页添加到主屏幕,在你访问了几个页面之后,浏览器会将这个Web应用“安装”到你的设备上。浏览几个页面,关闭Chrome并将设备与电脑断开连接,点击桌面上生成的图标,你会看到一个Splash页面,并且你可以继续浏览之前浏览过的页面。

图片 7图片 8

图片 9

不必提交应用软件商店

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

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

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

2、网络请求(Fetch
fetch()允许您发出类似于XMLHttpRequest(XHR)的网络请求。
主要区别在于Fetch
API使用Promises,它使用一个更简单和更清洁的API,避免了回调和不得不记住XMLHttpRequest的复杂API的苦境。
简单代码示例:

Activate 事件

这个事件会在service
worker被激活时发生。你可能不需要这个事件,但是在示例代码中,我们在该事件发生时将老的缓存全部清理掉了:

// clear old caches function clearOldCaches() { return caches.keys()
.then(keylist => { return Promise.all( keylist .filter(key => key
!== CACHE) .map(key => caches.delete(key)) ); }); } // application
activated self.addEventListener(‘activate’, event => {
console.log(‘service worker: activate’); // delete old caches
event.waitUntil( clearOldCaches() .then(() => self.clients.claim())
); });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// clear old caches
function clearOldCaches() {
  return caches.keys()
    .then(keylist => {
      return Promise.all(
        keylist
          .filter(key => key !== CACHE)
          .map(key => caches.delete(key))
      );
    });
}
// application activated
self.addEventListener(‘activate’, event => {
  console.log(‘service worker: activate’);
    // delete old caches
  event.waitUntil(
    clearOldCaches()
    .then(() => self.clients.claim())
    );
});

注意self.clients.claim()执行时将会把当前service
worker作为被激活的worker。

Fetch 事件
该事件将会在网络开始请求时发起。该事件处理函数中,我们可以使用respondWith()方法来劫持HTTP的GET请求然后返回:

  1. 从缓存中取到的资源文件
  2. 如果第一步失败,资源文件将会从网络中使用Fetch API来获取(和service
    worker中的fetch事件无关)。获取到的资源将会加入到缓存中。
  3. 如果第一步和第二步均失败,将会从缓存中返回正确的资源文件。

// application fetch network data self.addEventListener(‘fetch’, event
=> { // abandon non-GET requests if (event.request.method !== ‘GET’)
return; let url = event.request.url; event.respondWith(
caches.open(CACHE) .then(cache => { return cache.match(event.request)
.then(response => { if (response) { // return cached file
console.log(‘cache fetch: ‘ + url); return response; } // make network
request return fetch(event.request) .then(newreq => {
console.log(‘network fetch: ‘ + url); if (newreq.ok)
cache.put(event.request, newreq.clone()); return newreq; }) // app is
offline .catch(() => offlineAsset(url)); }); }) ); });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// application fetch network data
self.addEventListener(‘fetch’, event => {
  // abandon non-GET requests
  if (event.request.method !== ‘GET’) return;
  let url = event.request.url;
  event.respondWith(
    caches.open(CACHE)
      .then(cache => {
        return cache.match(event.request)
          .then(response => {
            if (response) {
              // return cached file
              console.log(‘cache fetch: ‘ + url);
              return response;
            }
            // make network request
            return fetch(event.request)
              .then(newreq => {
                console.log(‘network fetch: ‘ + url);
                if (newreq.ok) cache.put(event.request, newreq.clone());
                return newreq;
              })
              // app is offline
              .catch(() => offlineAsset(url));
          });
      })
  );
});

offlineAsset(url)方法中使用了一些helper方法来返回正确的数据:

// 是否为图片地址? let iExt = [‘png’, ‘jpg’, ‘jpeg’, ‘gif’, ‘webp’,
‘bmp’].map(f => ‘.’ + f); function isImage(url) { return
iExt.reduce((ret, ext) => ret || url.endsWith(ext), false); } //
return 返回离线资源 function offlineAsset(url) { if (isImage(url)) { //
返回图片 return new Response( ‘<svg role=”img” viewBox=”0 0 400 300″
xmlns=”
d=”M0 0h400v300H0z” fill=”#eee” /><text x=”200″ y=”150″
text-anchor=”middle” dominant-baseline=”middle” font-family=”sans-serif”
font-size=”50″ fill=”#ccc”>offline</text></svg>’, {
headers: { ‘Content-Type’: ‘image/svg+xml’, ‘Cache-Control’: ‘no-store’
}} ); } else { // return page return caches.match(offlineURL); } }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 是否为图片地址?
let iExt = [‘png’, ‘jpg’, ‘jpeg’, ‘gif’, ‘webp’, ‘bmp’].map(f => ‘.’ + f);
function isImage(url) {
  
  return iExt.reduce((ret, ext) => ret || url.endsWith(ext), false);
  
}
  
  
// return 返回离线资源
function offlineAsset(url) {
  
  if (isImage(url)) {
  
    // 返回图片
    return new Response(
      ‘<svg role="img" viewBox="0 0 400 300" xmlns="http://www.w3.org/2000/svg"><title>offline</title><path d="M0 0h400v300H0z" fill="#eee" /><text x="200" y="150" text-anchor="middle" dominant-baseline="middle" font-family="sans-serif" font-size="50" fill="#ccc">offline</text></svg>’,
      { headers: {
        ‘Content-Type’: ‘image/svg+xml’,
        ‘Cache-Control’: ‘no-store’
      }}
    );
  
  }
  else {
  
    // return page
    return caches.match(offlineURL);
  
  }
  
}

offlineAsset()方法检查请求是否为一个图片,然后返回一个带有“offline”文字的SVG文件。其他请求将会返回
offlineURL 页面。

Chrome开发者工具中的ServiceWorker部分提供了关于当前页面worker的信息。其中会显示worker中发生的错误,还可以强制刷新,也可以让浏览器进入离线模式。

Cache Storage
部分例举了当前所有已经缓存的资源。你可以在缓存需要更新的时候点击refresh按钮。

小结

通过本节对渐进式Web应用的介绍,相信大家对PWA是什么已经有了基本的认识。PWA有无需担心有无网络的特点,并具有独立入口与独立的保护机制。新标准的推出很可能会带着
Web 应用在移动设备上浴火重生。所以满足 PWA
模型的前端控件,如纯前端表格控件SpreadJS,将逐渐成为移动操作系统的一等公民,并将向Native
APP发起挑战。

在下节中我们将带你一起去看看,PWA的原理是什么,以及它究竟是如何工作的,敬请期待。

1 赞 1 收藏
评论

图片 10

              
  Fast:这一点应该都很熟悉了吧,站在用户的角度来考虑,如果一个网页加载速度有点长的话,那么我们会放弃浏览该网站,所以
PWA 在这一点上做的很好,他的加载速度是很快的。

面临的困难

缺乏通用支持

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

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

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

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

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

fetch('./api/some.json')
  .then(
    function(response) {
      if (response.status !== 200) {
        console.log('Looks like there was a problem. Status Code: ' +
          response.status);
        return;
      }

      // Examine the text in the response
      response.json().then(function(data) {
        console.log(data);
      });
    }
  )
  .catch(function(err) {
    console.log('Fetch Error :-S', err);
  });

第四步:创建可用的离线页面

离线页面可以是静态的HTML,一般用于提醒用户当前请求的页面暂时无法使用。然而,我们可以提供一些可以阅读的页面链接。

Cache
API可以在main.js中使用。然而,该API使用Promise,在不支持Promise的浏览器中会失败,所有的JavaScript执行会因此受到影响。为了避免这种情况,在访问/js/offlinepage.js的时候我们添加了一段代码来检查当前是否在离线环境中:

/js/offlinepage.js 中以版本号为名称保存了最近的缓存,获取所有URL,删除不是页面的URL,将这些URL排序然后将所有缓存的URL展示在页面上:

// cache name const CACHE = ‘::PWAsite’, offlineURL = ‘/offline/’, list
= document.getElementById(‘cachedpagelist’); // fetch all caches
window.caches.keys() .then(cacheList => { // find caches by and order
by most recent cacheList = cacheList .filter(cName =>
cName.includes(CACHE)) .sort((a, b) => a – b); // open first cache
caches.open(cacheList[0]) .then(cache => { // fetch cached pages
cache.keys() .then(reqList => { let frag =
document.createDocumentFragment(); reqList .map(req => req.url)
.filter(req => (req.endsWith(‘/’) || req.endsWith(‘.html’)) &&
!req.endsWith(offlineURL)) .sort() .forEach(req => { let li =
document.createElement(‘li’), a =
li.appendChild(document.createElement(‘a’)); a.setAttribute(‘href’,
req); a.textContent = a.pathname; frag.appendChild(li); }); if (list)
list.appendChild(frag); }); }) });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// cache name
const
  CACHE = ‘::PWAsite’,
  offlineURL = ‘/offline/’,
  list = document.getElementById(‘cachedpagelist’);
// fetch all caches
window.caches.keys()
  .then(cacheList => {
    // find caches by and order by most recent
    cacheList = cacheList
      .filter(cName => cName.includes(CACHE))
      .sort((a, b) => a – b);
    // open first cache
    caches.open(cacheList[0])
      .then(cache => {
        // fetch cached pages
        cache.keys()
          .then(reqList => {
            let frag = document.createDocumentFragment();
            reqList
              .map(req => req.url)
              .filter(req => (req.endsWith(‘/’) || req.endsWith(‘.html’)) && !req.endsWith(offlineURL))
              .sort()
              .forEach(req => {
                let
                  li = document.createElement(‘li’),
                  a = li.appendChild(document.createElement(‘a’));
                  a.setAttribute(‘href’, req);
                  a.textContent = a.pathname;
                  frag.appendChild(li);
              });
            if (list) list.appendChild(frag);
          });
      })
  });

图片 11

不列在应用商店目录中

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

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

3.缓存文件(Caching Files
Service Worker API带有一个Cache接口,可以让您创建按请求键入的响应存储。
虽然这个接口是面向服务人员的,但它实际上暴露在窗口中,并且可以从脚本中的任何地方访问。
入口点是缓存。
简单代码示例:

开发者工具

Chrome浏览器提供了一系列的工具来帮助您来调试Service
Worker,日志也会直接显示在控制台上。

您最好使用匿名模式来进行开发工作,这样可以排除缓存对开发的干扰。

最后,Chrome的Lighthouse扩展也可以为您的渐进式Web应用提供一些改进信息。

                 Engaging : PWA
可以添加在用户的主屏幕上,不用从应用商店进行下载(
似乎省了下载流量哦!)他们通过网络应用程序 Manifest file 提供类似于 APP
的使用体验( 在 Android 上可以设置全屏显示哦,由于 Safari
支持度的问题,所以在 IOS 上并不可以 ),并且还能进行 ”推送通知” 。     
                                                                

有限的本地硬件支持

与移动应用本地化设计不同,渐进式网页应用不能100%支持给定手机上的所有硬件功能。

虽然渐进式网页应用支持普通访问的功能,如加速器(Accelerometer)、摄像机和麦克风,但有一些功能需要由本机的移动应用来完成。

self.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open(cacheName).then(function(cache) {
      return cache.addAll(
        [
          '/css/bootstrap.css',
          '/css/main.css',
          '/js/bootstrap.min.js',
          '/js/jquery.min.js',
          '/offline.html'
        ]
      );
    })
  );
});

渐进式Web应用的要点

渐进式Web应用是一种新的技术,所以使用的时候一定要小心。也就是说,渐进式Web应用可以让您的网站在几个小时内得到改善,并且在不支持渐进式Web应用的浏览器上也不会影响网站的显示。

但是我们需要考虑以下几点:

图片 12

用户案例

AliExpress一直走在移动商务的前沿,一看到这一领域的增长,他们就迅速开发了自己的渐进式网页应用。他们通过渐进式网页应用获得的新用户转化率与其传统网站和移动应用相比增长了104%,看起来渐进式网页应用对这家中国公司相当适用。

Konga,一个尼日利亚电子商务网站,也在摸索开发渐进式网页应用。他们的目标是减少访问者的数据使用量,因为大约三分之二的尼日利亚用户仍然通过2G上网。据报道,他们的用户平均数据下载量减少了92%,Konga在这方面取得了成功。

eXtra
Electronic是一家沙特阿拉伯电子商务公司,他们已经开发了自己的渐进式网页应用,
目标是重新定位现有客户和访客。

最初他们一直在使用电子邮件广告活动推动重新参与策略,但现在他们已经添加了推送通知功能。他们发现用户重新参与度增加了4倍、点击率(click-through
rate)增加了12%、销售额增加了100%,而这些用户就来源于推送通知。

4、推送信息(Web Push Notifications
推送是基于Service Worker,因为Service
Worker在后台操作。它允许服务器向用户提示一些信息,并根据用户不同的行为进行一些简单的处理。
简单代码示例:

URL隐藏

当您的应用就是一个单URL的应用程序时(比如游戏),我建议您隐藏地址栏。除此之外的情况我并不建议您隐藏地址栏。在Manifest中,display: minimal-ui 或者 display: browser对于大多数情况来说足够用了。

         这三个主特性似乎能解释我在前言中所说的 ”在印度最流行“
。小小的普及一下,根据2016年第三季度的
IDC统计数据显示,印度的智能手机的出货量达到了3亿,同时也超过了美国,成为了全球第二大智能手机市场。皮尤斯数据报告
2015 年曾统计过“全球智能手机拥有率”。印度只有 17%
的人口用的是智能手机。虽然绝大部分人有手机用,但为非智能机,另有 22%
的人没有手机。针对印度的移动网络流量来说,网络状况是非常不佳的,根据16年的
GSMA 印度移动经济报告显示,网络覆盖率为34.8%
。主要集中在城镇,印度广大的农村还处于网络盲区,城乡数字化鸿沟很大。在印度,2G/3G
最主流,且 2G 占比还远高于 3G , 所以针对这种状态来说,PWA
技术最适合不过了。 

总结

总而言之,渐进式网页应用不会取代网站或移动应用。相反,渐进式网页应用已经在弥合两者之间的差异方面做得很好。有些企业可能需要一个完整的移动应用来实现完整的功能,而其他企业可能只需要一个标准的网站。

渐进式网页应用提供了两方面的最佳途径,如果我们要捕捉和留住没有网络连接的用户,无需提交应用商店就可以推送通知,以及为企业寻找最新的竞争优势,那可能只有它能做到这一点了。

切记,这是一种相对较新的技术,正处于开发和研究的早期阶段。可以将这项技术与当年的开发大西部类比,人们正在推动极限,每天都对新的领域进行探索。

如果不是因为谷歌正在积极寻找成功案例以便在自己网站上展示的话,在任何领域推出第一个渐进式网页应用都会有巨大的市场潜力。

如果您想了解这项新技术的更多信息,请访问以下链接:

Google PWA Study (2016年7月)

Google developer pages

Develop your first progressive web app

let options = {
  "body": "Did you make a $1,000,000 purchase at Dr. Evil...",
  "icon": "images/ccard.png",
  "vibrate": [200, 100, 200, 100, 200, 100, 400],
  "tag": "request",
  "actions": [
    { "action": "yes", "title": "Yes", "icon": "images/yes.png" },
    { "action": "no", "title": "No", "icon": "images/no.png" }
  ]
}
serviceWorkerRegistration.showNotification(title, options);

缓存过大

你不能将您网站中的所有内容缓存下来。对于小一些的网站来说缓存所有内容并不是一个问题,但是如果一个网站包含了上千个页面呢?很明显不是所有人对网站中的所有内容都感兴趣。存储是有限制的,如果您将所有访问过的页面都缓存下来的话,缓存大小会增长额很快。

你可以这样制定你的缓存策略:

  • 只缓存重要的页面,比如主页,联系人页面和最近浏览文章的页面。
  • 不要缓存任何图片,视频和大文件
  • 定时清理旧的缓存
  • 提供一个“离线阅读”按钮,这样用户就可以选择需要缓存哪些内容了。

        在我对 PWA 技术的研究中发现 PWA 其中有三个关键的技术:

缓存刷新

示例代码中在发起请求之前会先查询缓存。当用户处于离线状态时,这很好,但是如果用户处于在线状态,那他只会浏览到比较老旧的页面。

各种资源比如图片和视频不会改变,所以一般都把这些静态资源设置为长期缓存。这些资源可以直接缓存一年(31,536,000秒)。在HTTP
Header中,就是:

Cache-Control: max-age=31536000

1
Cache-Control: max-age=31536000

页面,CSS和脚本文件可能变化的更频繁一些,所以你可以设置一个比较小的缓存超时时间(24小时),并确保在用户网络连接恢复时再次从服务器请求:

Cache-Control: must-revalidate, max-age=86400

1
Cache-Control: must-revalidate, max-age=86400

你也可以在每次网站发布时,通过改名的方式强制浏览器重新请求资源。

                Service Worker( ps:就叫做服务工厂吧,文章最后一条 URL
是 SW 的全面进阶,可以研究研究哦 )

小结

至此,相信你如果按照本文一步一步操作下来,你也可以很快把自己的Web应用转为PWA。在转为了PWA后,如果有使用满足
PWA
模型的前端控件的需求,你可以试试纯前端表格控件SpreadJS,适用于
.NET、Java 和移动端等平台的表格控件一定不会令你失望的。

原文链接:

1 赞 1 收藏
评论

图片 10

                Manifest (应用清单)

                Push Notification(推送通知)

        下面我一一介绍着三个关键的技术:

                Service Worker( 以下用SW来代替 ) :

                SW 是什么呢?这个是离线缓存文件。我们 PWA
技术使用的就是它!SW
是浏览器在后台独立于网页运行的脚本,它打开了通向不需要网页或用户交互的功能的大门,因为使用了它,才会有的那个
Reliable 特性吧,SW 作用于 浏览器于服务器之间,相当于一个代理服务器(
用一张图来表示一下他的位置 )。                        

图片 14

                为什么会用到 SW 呢?原声 App
拥有Web应用通常所不具备的富离线体验,定时默认更新,消息推送等功能,而 SW
标准让在 Web App 上拥有这些功能成为可能!

                跟 SW 相同的 API 还有 App Cache ,为什么不使用它呢? App
Cache 是有局限性的,比如说:它很容易得解决 single web page application
( 单页面应用 )的问题,但是在多页面应用上会很麻烦, SW 解决的这个 App
Cache 的缺点!

         下面我简单而详细的说一下 SW :

              1、 浏览器支持

                        顺便带一句:目前只能在 HTTPS
环境下才能使用SW,因为SW
的权利比较大,能够直接截取和返回用户的请求,所以要考虑一下安全性问题。

图片 15

               2、事件机制

图片 16

               3、 功能

                     SW的功能还是比较逆天的!

后台数据的同步

从其他域获取资源请求

接受计算密集型数据的更新,多页面共享该数据

客户端编译与依赖管理

后端服务的hook机制

根据URL模式,自定义模板

性能优化

消息推送

定时默认更新

地理围栏

               4、 生命周期

图片 17

                      Parsed ( 解析成功 ): 首次注册 SW
时,浏览器解决脚本并获得入口点,如果解析成功,就可以访问到 SW
注册对象,在这一点中我们需要在 HTML
页面中添加一个判断,判断该浏览器是否支持 SW 。

                      Installing ( 正在安装 ):SW
脚本解析完成之后,浏览器会尝试进行安装,installing 中 install
事件被执行,如果其中有 event.waitUntil ( ) 方法,则 installing
事件会一直等到该方法中的 Promise 完成之后才会成功,如果 Promise
被拒绝,则安装失败,SW会进入 Redundant( 废弃 )状态。

                      Installed / Waiting
(安装成功/等待中):如果安装成功,SW 将会进入这个状态。

                      Activating ( 正在激活 ):处于 waiting 状态的 SW
发生以下情况,将会进入 activating 状态中:

                              当前已无激活状态的 worker 、 SW脚本中的
self.skipWaiting()方法被调用 ( ps: self 是 SW
中作用于全局的对象,这个方法根据英文翻译过来也能明白什么意思啦,跳过等待状态
)、用户已关闭 SW 作用域下的所有页面,从而释放了当前处于激活状态的
worker、超出指定时间,从而释放当前处于激活状态的 worker 

                      Activated ( 激活成功 ):该状态,其成功接收了
document 全面控制的激活态 worker 。

                      Redundant ( 废弃
):这个状态的出现时有原因的,如果 installing 事件失败或者 activating
事件失败或者新的 SW 替换其成为激活态 worker 。installing 事件失败和
activating 事件失败的信息我们可以在 Chrome 浏览器的 DevTools 中查看:

                               ( ps:我这个是正常的状态下的
,错误的话会有 error 提示的 )

图片 18

              5、主要依赖

                    SW 作为现代浏览器的高级特性,依赖于 fetch 、promise
、CacheStorage、Cache、等浏览器的基础能力, Cache 提供了 Request /
Response 对象对的存储机制。CacheStorage 则提供了存储 Cache
对象的机制。                                                  
             

图片 19

            6、安全性问题

                 跨域请求支持:  SW
可以拦截它作用域内的所有请求,跨域资源也不例外,但是浏览器默认对跨域资源发起的是
no-cors 请求,得到的 response 是 opaque 的,
所以会导致我们无法判断跨域请求是否成功,以便进行缓存,因此我们需要修改
fetch 请求头部信息,添加 mode:’cors’ 标记。

                ( 名词解释一下喽,这部分知识是关于 fetch API
的,想了解更多的同学,自行搜索相关的知识哈 )

                no-cors:该模式允许来自 CDN
的脚本、其他域的图片和其他一些跨域资源,但是首先有个前提条件,就是请求的
method 只能是 HEAD 、GET 、POST 。此外,如果 ServiceWorkers
拦截了这些请求,它不能随意添加或者修改除这些之外 Header 属性。第三,JS
不能访问 Response 对象中的任何属性,这确保了跨域时 ServiceWorkers
的安全和隐私信息泄漏问题。

                opaque:Response 对象中 type 属性的值 , 在 ‘no-cors’
模式下请求了跨域资源,依靠服务端来做限制。

            分享给大家一个 SW 全面进阶的博文:

        Manifest ( 应用清单 )

              Web App Manifest 是一个 W3C 规范,它定义了一个基于 JSON 的
List 。Manifest 在 PWA 中的作用有:

                  能够将你浏览的网页添加到你的手机屏幕上

                  在 Android 上能够全屏启动,不显示地址栏 ( 由于 Iphone
手机的浏览器是 Safari ,所以不支持哦)

                  控制屏幕 横屏 / 竖屏 展示

                  定义启动画面

                  可以设置你的应用启动是从主屏幕启动还是从 URL 启动

                  可以设置你添加屏幕上的应用程序图标、名字、图标大小

        Push Notification ( 消息通知 )

              Push 和 Notification 是两个不同的功能,涉及到两个 API 。

              Notification 是浏览器发出的通知消息。

              Push 和 Notification
的关系,Push:服务器端将更新的信息传递给 SW ,Notification: SW
将更新的信息推送给用户。

        缺点:

              1、浏览器的支持度问题,尤其是 Safari
浏览器,这样就会导致我们在 IOS 系统手机上没办法体验 PWA 。( 谁让 ‘ 果果
’ 不是开源的呢 )

              2、根据国情来看哈,目前 Native App
的使用用户都已经习惯了,虽然会下载一下,但是现在 WiFi 到处都是了,毕竟
WiFi 的普及太快了。让用户使用 PWA 来替代 Native App 短时间会不适应的。

              3、消息推送问题,PWA的消息推送走的是 GCM( FCM
)通道。而国内 Google 是无法访问的。(只能翻墙了,但是工信部已经禁止使用
VPN 了。) 

              总体来说:

                    Google 的技术在国内推进比较缓慢,所以 PWA
在国内的发展是有多困难吧。

【 Demo 】

首先呢,我们用到有 Node 和 Ngrok 。Node
的使用以及安装我就不说啦,作为一名前端开发工程师肯定会使用的啦。不会使用也米有关系啦,我们有度娘呢,Ngrok
的安装以及使用我就直接共享一个URl吧:http://blog.csdn.net/tomcat\_2014/article/details/68944066(
ps:我就偷懒一下 )

           对了,我使用的是 Mac
本(我司配的啦),所以我接下来的流程和截图都是 Mac 本上的。

           我们先创建一个关于 PWA 的项目文件夹,

                进入文件夹下我们准备一张
120×120的图片一张,作为我们的应用程序图标。

                创建一个 index.html  文件

                创建一个 index.css 文件

                创建一个 manifest.json 文件

                创建一个 sw.js 文件

                利用终端,安装一下 http-server 服务

                下面我们看一下每个文件中的代码是什么吧:

                index.html

图片 20

                index.css

图片 21

                manifest.json

                    short_name: “ ” 用户主屏幕上的应用名字

                    display : “standalone”
 设置启动样式,让您的网络应用隐藏浏览器的 URL 地址栏

                    start_url : “/“
设置启动网址,如果不提供的话,默认是使用当前页面

                    theme_color :
“ “  用来告知浏览器用什么颜色来为地址栏等 UI 元素着色

                    background_color: “ ” 设置启动页面的背景颜色

                    icons:””  就是添加到主屏幕之后的图标

图片 22

                sw.js

               
处理静态缓存,首先定义需要缓存的路径,以及需要缓存的静态文件的列表。

                借助 SW 注册完成安装 SW
时,抓取资源写入缓存中。使用了一个方法那就是 self.skipWaiting( )
这个方法我在前边介绍的时候也说了,为了在页面更新的过程当中,新的 SW
脚本能够立刻激活和生效。

图片 23

                 处理动态缓存,我们监听 fetch 事件,在 caches 中去 match
事件的 request ,如果 response 不为空的话就返回 response ,最后返回
fetch 请求,在 fetch 事件中我们可以手动生成 response 返回给页面。

               
 更新静态资源,缓存的资源会跟随着版本的更新会过期的,所以会根据缓存的字符串名称清除旧缓存。在新安装的
SW 中通过调用 self.clients.claim( )
取得页面的控制权,这样之后打开页面都会使用版本更新的缓存。旧的 SW
脚本不在控制着页面之后会被停止,也就是会进入 Redundant 期。

图片 24

               
以上截图中最左侧是我文件下的列表喽,下面我们来运行一下,终端启动
http-server 服务,我们以关闭缓存的方式进行启动。

图片 25

                接下来我们使用 ngrok 这个工具,进行内网穿透。在上边启动
http-server 服务的时候我们使用默认的端口号 8080 。所以我们在 ngrok
中我们绑定 端口 8080 。输入命令 : ./ngrok http 8080
之后我们看一下。(记得再打开一个终端哦,在另一个终端中进行操作)

图片 26

                我们看到绿色的字母 online
表示内网穿透成功了,我们看最后一个 Forwarding     https://
的,因为我们在上边介绍了,SW
的权利比较大,为了保证信息的安全性,我们使用 https
协议来进行访问。我们把它复制下来在 chrom 浏览器中打开,->
符号后边的就不用复制了哈。( 每个人的 ‘隧道’
都是不一样的哦,这一点同学们可以在 ngrok 官网中进行查询哦 )。

                 我们打开 chrom 的调试工具,打开 application ,点击
service workers 之后我们会发现 sw.js 脚本已经存到了 SW 中 。

图片 27

                我们打开 Network 刷新页面一下,看看,我们的页面资源来自
SW 而不是其他的地方,在 Console 中也打印出了我们在 index.html
中判断的语句,浏览器支持就会打印出这一句话哦。

图片 28

                接下来我们断网操作,在 Application 中给 Offline
打上对勾就行啦。然后刷新页面,我们仍然能看到之前的页面,原因就是我们在上图看到,他的资源是从
SW 上获得到的。当我们第一次打开这个页面的时候,Resopnse 对象被存到了
Cache Storage ( 定义在 SW 规范中 ,相关资料请同学们自行查询啦
)中,我们看下图:

图片 29

                通过存放到 Cache Storage
中,我们下次访问的时候如果是弱网或者断网的情况下,就可以不走网络请求,而直接就能将本地缓存的内容展示给用户,优化用户的弱网及断网体验。

               
这个时候肯定会有同学在想,如果内容更新了,那么页面展示的内容是新内容呢还是旧内容呢?下面我们操作一下,打开
index.html 文件,我们在 body 中添加一个 p 标签 ,然后回到页面刷新。

图片 30

图片 31

                我们看到,页面上的内容并没有显示出我刚刚添加的那个 p
标签。这说明了,我们拿到的数据还是从 Cache Storage 中获取到的,Cache
Storage中的内容并没有更新,那么我们怎么才能让我刚刚添加的那个 p
标签显示出来呢。

                我们打开 sw.js 脚本文件,我们修改一下 cacheStorageKey。

图片 32

               
我们关闭一下浏览器,然后再次打开该网址,页面出现了我们刚刚添加的那个 p
标签。我们再看一下 Cache Storage 中的缓存名字,已经被修改。

图片 33

【 总结 】

        总结一下吧,在研究这个 PWA
的过程中,搜索了相关的一大部分知识,就怕自己的脑洞不够大。感觉 PWA
涉及到的 API 比较多。要想研究透彻 PWA 还需要研究它所涉及到的 API
,慢慢研究吧。npm 中已经有这个包了哦。想真实的对 PWA
做深入研究的同学,可以应用到实际项目中。( 我感觉,会有很多坑
),不过呢,作为一名前端开发工程师,对于这种技术研究来说,当然是有坑就补,没坑挖坑补。我会在接下来的日子中,抽出时间对
PWA 进行深入研究的。当然也会共享给大家。

        上文中如果有说错的,或者错误的,欢迎各位大神指出!可以直接加我
QQ :568815707 ,说明意图哦,不然会拒绝的。

参考URL( 有的是需要翻墙的哦 ):

https://developers.google.com/web/progressive-web-apps/

http://foio.github.io/service-worker-cache/

http://bubkoo.com/2015/05/08/introduction-to-fetch/

http://www.dongcoder.com/detail-437618.html

http://imweb.io/topic/56592b8a823633e31839fc01

https://75team.com/post/lifecycle.html

https://segmentfault.com/a/1190000006061528

https://www.w3ctech.com/topic/866

http://dongcoder.com/detail-397355.html

https://www.npmjs.com/package/web-pwa

https://www.villainhr.com/tag/SW

发表评论

电子邮件地址不会被公开。 必填项已用*标注