革新你的网站,变身 PWA

《革新你的网站,变身 PWA》

近来有许多关于 Progressive Web Apps(PWAs)的音讯,许多人都在问这是不是是(挪动)web 的将来。我不想堕入native app 和 PWA 的纷争,然则有一件事是肯定的 — PWA极大的提升了挪动端表现,改良了用户体验。

好音讯是开辟一个 PWA 并不难。事实上,我们能够将现存的网站举行革新,使之成为PWA。这也是我这篇文章要讲的 — 当你读完这篇文章,你能够将你的网站革新,让他看起来就像是一个 native web app。他能够离线事情而且具有本身的主屏图标。

Progressive Web Apps 是什么?

Progressive Web Apps (下文以“PWAs”代指) 是一个令人兴奋的前端手艺的革新。PWAs综合了一系列手艺使你的 web app表现得就像是 native mobile app。比拟于纯 web 解决方案和纯 native 解决方案,PWAs关于开辟者和用户有以下长处:

  1. 你只须要基于开放的 W3C 规范的 web 开辟手艺来开辟一个app。不须要多客户端开辟。

  2. 用户能够在装置前就体验你的 app。

  3. 不须要经由过程 AppStore 下载 app。app 会自动晋级不须要用户晋级。

  4. 用户会遭到‘装置’的提示,点击装置会增添一个图标到用户首屏。

  5. 被翻开时,PWA 会展现一个有吸引力的闪屏。

  6. chrome 供应了可选选项,能够使 PWA 获得全屏体验。

  7. 必要的文件会被当地缓存,因而会比规范的web app 相应更快(也许也会比native app相应快)

  8. 装置及其轻量 — 也许会有几百 kb 的缓存数据。

  9. 网站的数据传输必需是 https 衔接。

  10. PWAs 能够离线事情,而且在收集恢复时能够同步最新数据。

如今还处在 PWA 的初期,但已有 许多胜利案例

PWA 手艺现在被 Firefox,Chrome 和其他基于Blink内核的阅读器支撑。微软正在勤奋在Edge阅读器上完成。Apple没有行动 although there are promising comments in the WebKit five-year plan。荣幸的是,阅读器支撑关于 PWA 好像不太重要…

PWAs 是渐进加强的

你的app依旧能够运转在不支撑 PWA 手艺的阅读器里。用户不能离线接见,不过其他功用都像本来一样没有影响。综合利弊得失,没有来由不把你的 app 革新为 PWA。

不只是 Apps

Google 引领了 PWA 的一系列行动,所以大多半教程都在说怎样从零开始构建一个基于 Chrome,native-looking mobile app。然则并非只要特别的单页运用能够PWA化,也不须要肯定遵照 material interface design guidelines。大多半网站都能够在数小时内完成 PWA 化。这包括你的 WordPress站点或许静态站点。

示例代码

示例代码能够在https://github.com/sitepoint-editors/pwa-retrofit找到。

代码供应了一个简朴的四个页面的网站。个中包括一些图片,一个样式表和一个main javascript 文件。这个网站能够运转在一切当代阅读器上(IE10+)。假如阅读器支撑 PWA 手艺,当离线时用户能够阅读他们之前看过的页面。

运转代码前,确保 Node.js 已装置,然后再命令行里启动效劳:

node ./server.js [port]

[port]是可设置的,默以为 8888。翻开 Chrome 或许其他基于Blink内核的阅读器,比方 Opera 或许 Vivaldi,然后输入链接 http://localhost:8888/(或许你指定的某个端口)。你也能够翻开开辟者东西看一下各个console信息。

《革新你的网站,变身 PWA》

阅读主页,或许其他页面,然后用以下任一要领使页面离线:

  1. 按下 Cmd/Ctrl + C ,住手 node 效劳器,或许

  2. 在开辟者东西的 Network 或许 Application – Service Workers 栏里点击 offline 选项。

从新阅读恣意之前阅读过的页面,它们依旧能够阅读到。阅读一个之前没有看过的页面,你会看到一个特地的离线页面,标识“you’re offline”,另有一个你能够阅读的页面列表:

《革新你的网站,变身 PWA》

衔接手机

你也能够经由过程 USB 衔接你的安卓手机来预览示例网页。在开辟者东西中翻开 Remote devices 菜单。

《革新你的网站,变身 PWA》

在左侧挑选 Settings ,点击 Add Rule 输入 8888 端口。你能够在你的手机上翻开Chrome,翻开 http://localhost:8888/

你能够点击阅读器菜单里的 “Add to Home screen”。阅读几个页面,阅读器会提示你去装置。这两种体式格局都能够建立一个新的图标在你的主屏上。阅读几个页面后关掉Chrome,断开装备衔接。你依旧能够翻开 PWA Website app — 你会看到一个启动页,而且能够离线接见之前你接见过的页面。

将你的网站革新为一个 Progressive Web App 总共有三个必要步骤:

第一步:开启 HTTPS

由于一些不言而喻的缘由,PWAs 须要 HTTPS 衔接。

HTTPS 在示例代码中并非必需的,由于 Chrome 许可运用 localhost 或许任何 127.x.x.x 的地点来测试。你也能够在 HTTP 衔接下测试你的 PWA,你须要运用 Chrome ,而且输入以下命令行参数:

  • --user-data-dir

  • --unsafety-treat-insecure-origin-as-secure

第二步:建立一个 Web App Manifest

manifest 文件供应了一些我们网站的信息,比方 name,description 和须要在主屏运用的图标的图片,启动屏的图片等。

manifest文件是一个 JSON 花样的文件,位于你项目标根目录。它必需用Content-Type: application/manifest+json 或许 Content-Type: application/json 如许的 HTTP 头来要求。这个文件能够被命名为任何名字,在示例代码中他被命名为 /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>中引入:

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

manifest 中重要属性有:

  • name —— 网页显现给用户的完全称号

  • short_name —— 当空间不足以显现全名时的网站缩写称号

  • description —— 关于网站的详细形貌

  • start_url —— 网页的初始 相对 URL(比方 /

  • scope —— 导航局限。比方,/app/的scope就限定 app 在这个文件夹里。

  • background-color —— 启动屏和阅读器的背景色彩

  • theme_color —— 网站的主题色彩,平常都与背景色彩雷同,它能够影响网站的显现

  • orientation —— 首选的显现方向:any, natural, landscape, landscape-primary, landscape-secondary, portrait, portrait-primary, 和 portrait-secondary

  • display —— 首选的显现体式格局:fullscreen, standalone(看起来像是native app),minimal-ui(有简化的阅读器掌握选项) 和 browser(通例的阅读器 tab)

  • icons —— 定义了 src URL, sizestype的图片对象数组。

MDN供应了完全的manifest属性列表:Web App Manifest properties

在开辟者东西中的 Application tab 左侧有 Manifest 选项,你能够考证你的 manifest JSON 文件,并供应了 “Add to homescreen”。

《革新你的网站,变身 PWA》

第三步:建立一个 Service Worker

Service Worker 是阻拦和相应你的收集要求的编程接口。这是一个位于你根目录的一个零丁的 javascript 文件。

你的 js 文件(在示例代码中是 /js/main.js)能够搜检是不是支撑 Service Worker,而且注册:

if ('serviceWorker' in navigator) {

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

}

假如你不须要离线功用,能够简朴的建立一个空的 /service-worker.js文件 —— 用户会被提示装置你的 app。

Service Worker 很庞杂,你能够修正示例代码来到达本身的目标。这是一个规范的 web worker,阅读器用一个零丁的线程来下载和实行它。它没有挪用 DOM 和其他页面 api 的才能,但他能够阻拦收集要求,包括页面切换,静态资本下载,ajax要求所引发的收集要求。

这就是须要 HTTPS 的最重要的缘由。设想一下第三方代码能够阻拦来自其他网站的 service worker, 将是一个灾害。

service worker 重要有三个事宜: installactivatefetch

Install 事宜

这个事宜在app被装置时触发。它常常用来缓存必要的文件。缓存经由过程 Cache API来完成。

起首,我们来组织几个变量:

  1. 缓存称号(CACHE)和版本号(version)。你的运用能够有多个缓存然则只能援用一个。我们设置了版本号,如许当我们有严重更新时,我们能够更新缓存,而疏忽旧的缓存。

  2. 一个离线页面的URL(offlineURL)。当离线时用户试图接见之前未缓存的页面时,这个页面会显现给用户。

  3. 一个具有离线功用的页面必要文件的数组(installFilesEssential)。这个数组应当包括静态资本,比方 CSS 和 JavaScript 文件,但我也把主页面(/)和图标文件写进去了。假如主页面能够多个URL接见,你应当把他们都写进去,比方//index.html。注重,offlineURL也要被写入这个数组。

  4. 可选的,形貌文件数组(installFilesDesirable)。这些文件都很会被下载,但假如下载失利不会中断装置。

// 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'
  ];

installStaticFiles()要领增加文件到缓存,这个要领用到了基于 promise的 Cache 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要领确保一切代码实行终了后,service worker 才会实行 install。实行 installStaticFiles()要领,然后实行 self.skipWaiting()要领使service worker进入 active状况。

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

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

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

});

Activate 事宜

当 install完成后, service worker 进入active状况,这个事宜马上实行。你能够不须要完成这个事宜监听,然则示例代码在这里删除老旧的无用缓存文件:

// 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()要领设置本身为active的service worker。

Fetch 事宜

当有收集要求时这个事宜被触发。它挪用respondWith()要领来挟制 GET 要求并返回:

  1. 缓存中的一个静态资本。

  2. 假如 #1 失利了,就用 Fetch API(这与 service worker 的fetch 事宜没紧要)去收集要求这个资本。然后将这个资本到场缓存。

  3. 假如 #1 和 #2 都失利了,那就返回一个恰当的值。

// 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)要领经由过程几个辅佐函数返回一个恰当的值:

// is image URL?
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 offline asset
function offlineAsset(url) {

  if (isImage(url)) {

    // return image
    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 页面。

开辟者东西供应了检察 Service Worker 相干信息的选项:

《革新你的网站,变身 PWA》

在开辟者东西的 Cache Storage 选项列出了一切当前域内的缓存和所包括的静态文件。当缓存更新的时刻,你能够点击左下角的革新按钮来更新缓存:

《革新你的网站,变身 PWA》

不出预料, Clear storage 选项能够删除你的 service worker 和缓存:

《革新你的网站,变身 PWA》

再来一步 – 第四步:建立一个可用的离线页面

离线页面能够是一个静态页面,来申明当前用户要求不可用。然则,我们也能够在这个页面上列出能够接见的页面链接。

main.js中我们能够运用 Cache API 。然则API 运用promises,在不支撑的阅读器中会引发一切javascript运转壅塞。为了防止这类状况,我们在加载另一个 /js/offlinepage.js 文件之前必需搜检离线文件列表和是不是支撑 Cache API 。

// 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);
}

/js/offlinepage.js locates the most recent cache by version name, 取到一切 URL的key的列表,移除一切无用 URL,排序一切的列表而且把他们加到 ID 为cachedpagelist的 DOM 节点中:

// 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);

          });

      })

  });

开辟东西

假如你以为 javascript 调试难题,那末 service worker 也不会很好。Chrome的开辟者东西的 Application 供应了一系列调试东西。

你应当翻开 隐身窗口 来测试你的 app,如许在你封闭这个窗口以后缓存文件就不会保留下来。

末了,Lighthouse extension for Chrome 供应了许多革新 PWA 的有效信息。

PWA 圈套

有几点须要注重:

URL 隐蔽

我们的示例代码隐蔽了 URL 栏,我不引荐这类做法,除非你有一个单 url 运用,比方一个游戏。关于多半网站,manifest 选项 display: minimal-ui 或许 display: browser是最好的挑选。

缓存太多

你能够缓存你网站的一切页面和一切静态文件。这关于一个小网站是可行的,但这关于上千个页面的大型网站现实吗?没有人会对你网站的一切内容都感兴趣,而装备的内存容量将是一个限定。纵然你像示例代码一样只缓存接见过的页面和文件,缓存大小也会增进的很快。

也许你须要注重:

  • 只缓存重要的页面,相似主页,和近来的文章。

  • 不要缓存图片,视频和其他大型文件

  • 常常删除旧的缓存文件

  • 供应一个缓存按钮给用户,让用户决议是不是缓存

缓存革新

在示例代码中,用户在要求收集前先搜检该文件是不是缓存。假如缓存,就运用缓存文件。这在离线状况下很棒,但也意味着在联网状况下,用户获得的能够不是最新数据。

静态文件,相似于图片和视频等,不会常常转变的资本,做长时候缓存没有很大的题目。你能够在HTTP 头里设置 Cache-Control 来缓存文件使其缓存时候为一年(31,536,000 seconds):

Cache-Control: max-age=31536000

页面,CSS和 script 文件会常常变化,所以你应当改设置一个很短的缓存时候比方 24 小时,并在联网时与效劳端文件举行考证:

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

译自 Retrofit Your Website as a Progressive Web App

    原文作者:MrDream
    原文地址: https://segmentfault.com/a/1190000008880637
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞