我不打算详细讨论原生应用与PWA之争,但有一件事是肯定的,它们在增强移动设备和改善用户体验方面效果明显。

移动互联网注定是大势所趋

开发一个PWA应用其实并不难,按照本文的步骤一步一步完成之后,就会完成一个PWA应用;可以离线工作,有自己的主屏幕图标。

背景

PWA混合了多种技术,使web应用程序的功能尽量接近本地移动应用。为开发人员和用户带来的好处是克服了只使用web和只使用本地解决方案所带来的限制。

先看一些成功的案例:印度最大的电子商务网站Flipkart的销售转化率增长了70%,当他们放弃自己的原生应用,转而使用PWA时,他们的现场时间增加了两倍。全球最大的交易平台阿里巴巴的转化率也增长了76%。

Firefox、Chrome和其他基于blink的浏览器都提供了可靠的PWA技术支持。微软正在开发在Edge上的实现。尽管PWA在WebKit的五年计划中有一些很有前途的评论,苹果却仍然保持沉默。幸运的是,浏览器支持基本上是无关紧要的……

网站可以在不支持PWA技术的浏览器中运行。用户仅仅不会获得离线功能,但站点依然会正常工作。考虑到PWA的超高性价比,没理由不将PWA技术应用到网站中。

PWA实战

它就是一个简单的网站,包含一些图片、一个样式表和一个主JavaScript文件。本网站适用于所有现代浏览器(IE10+)。如果浏览器支持PWA技术,用户可以在离线状态下阅读访问过的页面。

所有代码托管在GitHub

想要运行示例代码,要确保Node.js安装,在命令行运行如下命令:

node ./server.js [port]

port参数是可以选的如果不指定,默认就是:8888。

在浏览器输入http://localhost:8888/即可查看。

总体来说,将站点转换成进步的Web应用程序,需要遵循以下四步:

步骤1:https

你需要通过 HTTPS 来访问你的页面 — 出于安全原因,Service Workers 要求必须在 HTTPS 下才能运行。Github 是个用来测试的好地方,因为它就支持HTTPS。为了便于本地开发,localhost 也被浏览器认为是安全源。

步骤2:创建manifest文件

应用程序清单提供了关于应用程序的信息,例如名称、描述和图像,操作系统使用这些信息配置主屏幕图标、启动页和视窗。从本质上说,清单是一个文件,可以替代您在页面中已经拥有的众多特定于供应商的图标和主题标记。

manifest是一个json文件,存放在web根目录下。它的MIME类型要么是:Content-Type: application/manifest+json,或者是Content-Type: application/json。

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"         : "72x72",
      "type"          : "image/png"
    },
    {
      "src"           : "/images/logo/logo152.png",
      "sizes"         : "152x152",
      "type"          : "image/png"
    },
    {
      "src"           : "/images/logo/logo192.png",
      "sizes"         : "192x192",
      "type"          : "image/png"
    },
    {
      "src"           : "/images/logo/logo256.png",
      "sizes"         : "256x256",
      "type"          : "image/png"
    },
    {
      "src"           : "/images/logo/logo512.png",
      "sizes"         : "512x512",
      "type"          : "image/png"
    }
  ]
}

页面head中引入manifest.json:

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

主要属性如下:

  • name:将显示给用户的应用程序的全名
  • short_name:用于全名空间不足的情况
  • description:对应用程序的详细描述
  • start_url:启动应用程序的相对URL(通常为/)
  • scope:导航作用域——例如,/app/的作用域将应用程序限制在该文件夹内
  • background_color:用于启动屏幕和浏览器chrome的背景颜色(如果需要)
  • theme_color:应用程序的颜色,通常与背景相同,这会影响应用程序的显示方式
  • orientation:首选朝向——任意、自然、景观、景观—主要、景观—次要、肖像、肖像—主要、肖像—次要
  • display:首选视图-全屏(没有chrome),独立(看起来像一个本地应用程序),最小UI(一组小的UI控件)和浏览器(一个传统的浏览器选项卡)
  • icons:定义src URL、大小和类型的图像对象数组(应该定义图标的范围)。

详细介绍请查看PWA Manifest 属性

步骤3:创建Service Worker

Service workers 本质上充当Web应用程序与浏览器之间的代理服务器,也可以在网络可用时作为浏览器和网络间的代理。

首先判断浏览器是否支持service worker:

if ('serviceWorker' in navigator) {

  // register service worker
  navigator.serviceWorker.register('/service-worker.js');

}

它是一个标准的web worker脚本,浏览器可以下载并在单独的线程上运行。它不能访问DOM或BOM,但是可以拦截由页面更改、资源下载和Ajax调用触发的网络请求。

这就是您的站点需要HTTPS的主要原因。想象一下,如果一个第三方脚本可以从另一个域注入它自己的service worker,会造成多大的危害。它能够修改客户端和服务器之间的所有数据全部替换掉!

Service Worker 主要提供了三个事件:install, activatefetch

Install Event

在service worker安装时触发,它的作用是利用缓存API缓存基本文件。

首先,我们将定义一些配置变量:

// configuration
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'
  ];
// application installation
self.addEventListener('install', event => {

  console.log('service worker: install');

  // cache core files
  event.waitUntil(
    installStaticFiles()
    .then(() => self.skipWaiting())
  );

});

installStaticFiles() 函数的作用是:利用缓存API将文件添加到缓存中。返回值只有在基本文件被缓存时才会返回:

// install static assets
function installStaticFiles() {

  return caches.open(CACHE)
    .then(cache => {

      // cache desirable files
      cache.addAll(installFilesDesirable);

      // cache essential files
      return cache.addAll(installFilesEssential);

    });

}

最后,我们添加一个install监听。waitUntil方法确保servie worker安装成功。它先运行installStaticFiles(),然后运行self.skipWaiting()来激活service worker:

// application installation
self.addEventListener('install', event => {

  console.log('service worker: install');

  // cache core files
  event.waitUntil(
    installStaticFiles()
    .then(() => self.skipWaiting())
  );

});

Activate Event

当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())
    );

});

注意self.clients.claim()的作用是重新激活状态。

步骤4:创建离线页面

离线页面可以是静态HTML,用于通知用户所请求的页面离线不可用。我们还可以指定可供读取的页面url列表。

// load script to populate offline page list
if (document.getElementById('cachedpagelist') && 'caches' in window) {
  var scr = document.createElement('script');
  scr.src = '/js/offlinepage.js';
  scr.async = 1;
  document.head.appendChild(scr);
}

PWA 陷阱

渐进式Web应用是一种新技术,因此需要一些谨慎。也就是说,它们是对现有网站的改进,应该不会超过几个小时,而且不会对不受支持的浏览器产生负面影响。

有以下几点要注意:

Cache 过载

您可以缓存站点上的所有资源。这对于小型站点来说很好,但是对于那些大型站点来说是否可行呢? 没有人可能对您的所有内容感兴趣,而且过多缓存导致超出设备存储限制。即使只存储访问过的页面和资源(如演示),缓存也会增长很快。

  • 只缓存重要页面,如主页、联系人和最新文章
  • 不缓存图像、视频和其他大文件
  • 定期清除旧的缓存文件
  • 提供一个“储存此页以便脱机阅读”按钮,以便用户主动选择缓存什么

Cache 更新

资源加载时会首先从缓存中加载,当用户离线时,访问是迅速的,但这意味着他们可能读取的是缓存,即使他们在线时访问也是如此。

image和video等资源的url永远不变,长缓存很少有问题。使用缓存控制HTTP头,您可以确保它们保持缓存至少一年(31,536,000秒):

Cache-Control: max-age=31536000

页面、CSS和js文件由于更改频繁,所以可以设置更短的24小时,并确保它在联网时根据服务器版本进行验证:

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

您可以考虑使用强制缓存失效技术来确保引用新的资源——例如,每个版本中根据内容计算hash值并重命名为styles-abc123。

有用的链接

如果您想更多地了解渐进式Web应用,下面是一些有用的链接: