入门教程,升级指南

React 同构应用 PWA 升级指南

2018/05/25 · JavaScript
· PWA,
React

原稿出处:
林东洲   

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

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

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

上篇小说我们对渐进式Web应用(PWA)做了有些宗旨的介绍。

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

在这一节中,大家将介绍PWA的规律是怎么着,它是怎么着开首工作的。

应用 瑟维斯(Service) Worker 做一个 PWA 离线网页应用

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

原稿出处:
人人网FED博客   

在上一篇《自家是什么样让网站用上HTML5
Manifest》介绍了怎么用Manifest做一个离线网页应用,结果被大面积网友吐槽说那些事物已经被deprecated,移出web标准了,现在被瑟维斯(Service)(Service)Worker替代了,不管什么,Manifest的有些考虑依然得以借用的。笔者又将网站升级到了Service(Service)Worker,若是是用Chrome等浏览器就用Service(Service)Worker做离线缓存,如果是Safari浏览器就照旧用Manifest,读者可以打开那几个网站感受一下,断网也是能健康打开。

入门教程,升级指南。前方的话

  渐进式网络应用 ( Progressive Web Apps ),即大家所熟练的 PWA,是
谷歌(Google) 提议的用前沿的 Web 技术为网页提供 App 般使用体验的一多元方案。PWA
本质上是 Web App,借助一些新技巧也拥有了 Native App
的有些特点。本文将详细介绍针对现有网站的PWA升级

 

前言

前不久在给自家的博客网站 PWA 升级,顺便就记下下 React 同构应用在使用 PWA
时碰着的问题,那里不会从头起始介绍如何是 PWA,倘若您想上学 PWA
相关知识,可以看下上边我收藏的局地稿子:

  • 您的率先个 Progressive Web
    App
  • 【瑟维斯(Service)Worker】生命周期那几个事儿
  • 【PWA学习与执行】(1)
    2018,初阶你的PWA学习之旅
  • Progressive Web Apps (PWA)
    中文版

第一步:使用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

1. 什么是Service Worker

Service(Service) Worker是谷歌(Google)发起的兑现PWA(Progressive Web
App)的一个关键角色,PWA是为着缓解传统Web APP的后天不足:

(1)没有桌面入口

(2)无法离线使用

(3)没有Push推送

那Service(Service) Worker的具体表现是什么的吗?如下图所示:

亚洲必赢官网 1

瑟维斯(Service)(Service)Worker是在后台启动的一条服务Worker线程,上图我开了多个标签页,所以显示了两个Client,但是不管开多少个页面都只有一个Worker在负责管理。那一个Worker的干活是把有些资源缓存起来,然后拦截页面的伸手,先看下缓存库里有没有,即便局地话就从缓存里取,响应200,反之没有的话就走正规的央求。具体来说,ServiceWorker结合Web App Manifest能完毕以下工作(那也是PWA的检测专业):

亚洲必赢官网 2

概括可以离线使用、断网时回来200、能唤醒用户把网站添加一个图标到桌面上等。

效果演示

  在此往日端小站xiaohuochai.cc的PWA效果做示范,github移步至此

【添加到桌面】

亚洲必赢官网 3

【离线缓存】

   由于手机录屏选用无法展开离线录制,改由模拟器模拟离线效果

亚洲必赢官网 4

 

PWA 特性

PWA 不是单纯的某项技术,而是一堆技术的集纳,比如:瑟维斯(Service)Worker,manifest 添加到桌面,push、notification api 等。

而就在近期岁月,IOS 11.3 刚刚援助 Service(Service) worker 和相近 manifest
添加到桌面的表征,所以本次 PWA
改造重点仍然完毕那两有些成效,至于其余的特征,等 iphone 辅助了再升高吗。

其次步:创制一个应用程序清单(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,大小和档次。

2. 瑟维斯(Service) Worker的支撑意况

瑟维斯(Service) Worker如今唯有Chrome/Firfox/Opera支持:

亚洲必赢官网 5

Safari和Edge也在备选帮助瑟维斯(Service)(Service) Worker,由于瑟维斯(Service)Worker是谷歌(谷歌(Google))为主的一项正式,对于生态相比封闭的Safari来说也是迫于时势先导准备支持了,在Safari
TP版本,可以看出:

亚洲必赢官网 6

在实验作用(Experimental Features)里早已有ServiceWorker的菜单项了,只是即使打开也是无法用,会提醒您还从未落到实处:

亚洲必赢官网 7

但无论是如何,至少表达Safari已经准备帮忙ServiceWorker了。其余还足以见见在当年二〇一七年四月揭发的Safari
11.0.1版本已经协理WebRTC了,所以Safari依旧一个发展的男女。

Edge也准备扶助,所以Service Worker的前景极度美好。

概述

  PWA 的首要特征包蕴上面三点:

  1、可信 – 尽管在不安定的网络环境下,也能须臾间加载并显现

  2、体验 – 火速响应,并且有平整的动画片响应用户的操作

  3、粘性 –
像装备上的原生应用,具有沉浸式的用户体验,用户可以添加到桌面

  首要功能包含站点可添加至主显示器、全屏方式运行、扶助离线缓存、信息推送等

【PRPL模式】

  “PRPL”(读作 “purple”)是 谷歌 的工程师提出的一种 web
应用架构形式,它意在利用现代 web 平台的新技巧以大幅优化移动 web
的性质与经验,对什么社团与规划高性能的 PWA 系统提供了一种高层次的虚幻

  “PRPL”实际上是 Push/Preload、Render、Precache、Lazy-Load 的缩写

  1、PUSH/PRELOAD,推送/预加载先导 URL 路由所需的首要资源

  2、RENDER,渲染伊始路由,尽快让使用可被交互

  3、PRE-CACHE,用 Service Worker 预缓存剩下的路由

  4、LAZY-LOAD 按需懒加载、懒实例化剩下的路由

【Service workers】

  Service(Service) Workers 是Google chrome 团队提议并大力推广的一项 web 技术。在
2015 年,它进入到 W3C 标准,进入草案阶段

  PWA 的关键在于 Service Workers 。就其要旨来说,Service(Service) Workers
只是后台运行的 worker 脚本。它们是用 JavaScript
编写的,只需短短几行代码,它们便可使开发者可以阻止网络请求,处理推送音信并施行许多别样职分

  瑟维斯(Service) Worker 中用到的片段全局变量:

self: 表示 Service Worker 作用域, 也是全局变量
caches: 表示缓存
skipWaiting: 表示强制当前处在 waiting 状态的脚本进入 activate 状态
clients: 表示 Service Worker 接管的页面

  Service Worker 的干活机制大概如下:用户访问一个有所 瑟维斯(Service)(Service) Worker
的页面,浏览器就会下载那个 瑟维斯(Service) Worker
并尝试安装、激活。一旦激活,Service(Service) Worker
就到后台开端工作。接下来用户访问那些页面或者每隔一个时光浏览器都会下载那个瑟维斯(Service) Worker,若是监测到 瑟维斯(Service) Worker 有立异,就会重新安装并激活新的
瑟维斯(Service) Worker,同时 revoke 掉旧的 瑟维斯(Service)(Service) Worker,这就是 SW 的生命周期

  因为 瑟维斯 Worker 有着方今的权杖接触数据,因而 瑟维斯(Service)(Service) Worker
只好被设置在 HTTPS 加密的页面中,即便无形当中提升了 PWA
的门道,可是也是为了安全做考虑

 

Service Worker

service worker
在我看来,类似于一个跑在浏览器后台的线程,页面第四回加载的时候会加载那些线程,在线程激活之后,通过对
fetch 事件,能够对种种得到的资源拓展支配缓存等。

其三步:成立一个 Service Worker

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

您必要在页面对应的JavaScript文件中登记该瑟维斯(Service)Worker:

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应用了!

ServiceWorker这几个概念可能比较难懂,它实质上是一个干活在别的线程中的标准的Worker,它不得以访问页面上的DOM元素,没有页面上的API,可是足以阻止所有页面上的网络请求,包含页面导航,请求资源,Ajax请求。

下边就是拔取全站HTTPS的机要缘由了。假若你没有在你的网站中运用HTTPS,一个第三方的台本就足以从其它的域名注入他协调的ServiceWorker,然后篡改所有的央求——这活脱脱是那些惊险的。

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

3. 使用Service Worker

Service(Service)Worker的拔取套路是先挂号一个Worker,然后后台就会启动一条线程,能够在那条线程启动的时候去加载一些资源缓存起来,然后监听fetch事件,在那一个事件里拦截页面的伸手,先看下缓存里有没有,假设有直接重返,否则正常加载。或者是一先导不缓存,每个资源请求后再拷贝一份缓存起来,然后下两遍呼吁的时候缓存里就有了。

离线缓存

  下边来经过service worker达成离线缓存

  一般地,通过sw-precache-webpack-plugin插件来落成动态生成service
worker文件的作用

  不过,首先要在index.html中引用service worker

    <script>
      (function() {
        if('serviceWorker' in navigator) {
          navigator.serviceWorker.register('/service-worker.js');
        }
      })()
    </script>

【SPA】

  通过create-react-app生成的react
SPA应用默许就展开了sw-precache-webpack-plugin的安装。可是,其只对静态资源开展了安装

  假如是接口资源,则一般的拍卖是预先通过网络访问,借使网络不通,再经过service
worker的缓存举行走访

  webpack.config.prod.js文件的计划如下

    const SWPrecacheWebpackPlugin = require('sw-precache-webpack-plugin');
    new SWPrecacheWebpackPlugin({
      // By default, a cache-busting query parameter is appended to requests
      // used to populate the caches, to ensure the responses are fresh.
      // If a URL is already hashed by Webpack, then there is no concern
      // about it being stale, and the cache-busting can be skipped.
      dontCacheBustUrlsMatching: /\.\w{8}\./,
      filename: 'service-worker.js',
      logger(message) {
        if (message.indexOf('Total precache size is') === 0) {
          // This message occurs for every build and is a bit too noisy.
          return;
        }
        if (message.indexOf('Skipping static resource') === 0) {
          // This message obscures real errors so we ignore it.
          // https://github.com/facebookincubator/create-react-app/issues/2612
          return;
        }
        console.log(message);
      },
      minify: true,
      // For unknown URLs, fallback to the index page
      navigateFallback: publicUrl + '/index.html',
      // Ignores URLs starting from /__ (useful for Firebase):
      // https://github.com/facebookincubator/create-react-app/issues/2237#issuecomment-302693219
      navigateFallbackWhitelist: [/^(?!\/__).*/],
      // Don't precache sourcemaps (they're large) and build asset manifest:
      staticFileGlobsIgnorePatterns: [/\.map$/, /asset-manifest\.json$/],
      runtimeCaching: [{
          urlPattern: '/',
          handler: 'networkFirst'
        },
        {
          urlPattern: /\/api/,
          handler: 'networkFirst'
        }
      ]
    })

【SSR】

  假如是劳动器端渲染的采用,则布署基本相仿。但鉴于无法使用代理,则须要设置网站实际路径,且由于静态资源已经存到CDN,则缓存不再通过service
worker处理

  配置如下

    new SWPrecacheWebpackPlugin({
      dontCacheBustUrlsMatching: /\.\w{8}\./,
      filename: 'service-worker.js',
      logger(message) {
        if (message.indexOf('Total precache size is') === 0) {
          return;
        }
        if (message.indexOf('Skipping static resource') === 0) {
          return;
        }
        console.log(message);
      },
      navigateFallback: 'https://www.xiaohuochai.cc',
      minify: true,
      navigateFallbackWhitelist: [/^(?!\/__).*/],
      dontCacheBustUrlsMatching: /./,
      staticFileGlobsIgnorePatterns: [/\.map$/, /\.json$/],
      runtimeCaching: [{
          urlPattern: '/',
          handler: 'networkFirst'
        },
        {
          urlPattern: /\/(posts|categories|users|likes|comments)/,
          handler: 'networkFirst'
        },
      ]
    })
  ]

 

强烈怎样资源须要被缓存?

那么在开端应用 service worker 以前,首先需要精通什么资源需求被缓存?

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.add伊芙(Eve)ntListener(‘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())
  );
});

(1)注册一个Service(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)(Service)Worker和Cookie一样是有Path路径的定义的,尽管你设定一个cookie假使叫time的path=/page/A,在/page/B那个页面是不可见拿走到那些cookie的,如若设置cookie的path为根目录/,则有着页面都能获取到。类似地,假诺注册的时候使用的js路径为/page/sw.js,那么那么些ServiceWorker只好管理/page路径下的页面和资源,而不能处理/api路径下的,所以一般把瑟维斯(Service)Worker注册到五星级目录,如上面代码的”/sw-3.js”,那样那么些ServiceWorker就能接管页面的兼具资源了。

丰硕到屏幕

  没人愿意多此一举地在移动设备键盘上输入长长的网址。通过抬高到显示屏的成效,用户可以像从使用商店安装本机应用那样,选取为其配备加上一个火速链接,并且经过要顺遂得多

【配置项表明】

  使用manifest.json文件来落到实处拉长到屏幕的效应,上边是该公文内的配置项

short_name: 应用展示的名字
icons: 定义不同尺寸的应用图标
start_url: 定义桌面启动的 URL
description: 应用描述
display: 定义应用的显示方式,有 4 种显示方式,分别为:
  fullscreen: 全屏
  standalone: 应用
  minimal-ui: 类似于应用模式,但比应用模式多一些系统导航控制元素,但又不同于浏览器模式
  browser: 浏览器模式,默认值
name: 应用名称
orientation: 定义默认应用显示方向,竖屏、横屏
prefer_related_applications: 是否设置对应移动应用,默认为 false
related_applications: 获取移动应用的方式
background_color: 应用加载之前的背景色,用于应用启动时的过渡
theme_color: 定义应用默认的主题色
dir: 文字方向,3 个值可选 ltr(left-to-right), rtl(right-to-left) 和 auto(浏览器判断),默认为 auto
lang: 语言
scope: 定义应用模式下的路径范围,超出范围会以浏览器方式显示

  上面是一份健康的manifest.json文件的安顿

{
  "name": "小火柴的前端小站",
  "short_name": "前端小站",
  "start_url": "/",
  "display": "standalone",
  "description": "",
  "theme_color": "#fff",
  "background_color": "#d8d8d8",
  "icons": [{
      "src": "./logo_32.png",
      "sizes": "32x32",
      "type": "image/png"
    },
    {
      "src": "./logo_48.png",
      "sizes": "48x48",
      "type": "image/png"
    },
    {
      "src": "./logo_96.png",
      "sizes": "96x96",
      "type": "image/png"
    },
    {
      "src": "./logo_144.png",
      "sizes": "144x144",
      "type": "image/png"
    },
    {
      "src": "./logo_192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "./logo_256.png",
      "sizes": "256x256",
      "type": "image/png"
    }
  ]
}

【注意事项】

  1、在 Chrome 上首选使用 short_name,若是存在,则先行于 name
字段使用

  2、图标的花色最好是png,,且存在144px的尺码,否则会收获如下提醒

Site cannot be installed: a 144px square PNG icon is required, but no supplied icon meets this requirement

  3、start_url表示项目启动路径

  如果是’/’,则启动路径为

localhost:3000/

  要是是’/index.html’,则启动路径为

localhost:3000/index.html

  所以,最好填写’/’

【HTML引用】

   在HTML文档中经过link标签来引用manifest.json文件

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

  要更加注意manifest文件路径问题,要将该公文放到静态资源目录下,否则,会找不到该文件,控制台突显如下提醒

Manifest is not valid JSON. Line: 1, column: 1, Unexpected token

  如若index.html也坐落静态资源目录,则设置如下

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

  若是index.html位于根目录,而静态资源目录为static,则设置如下

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

【meta标签】

  为了更好地SEO,需求经过meta标签设置theme-color

<meta name="theme-color" content="#fff"/>

【SSR】

  若是是劳动器端配置,需求在server.js文件中布署manifest.json、logo、icon等文件的静态路径

app.use(express.static(path.join(__dirname, 'dist')))
app.use('/manifest.json', express.static(path.join(__dirname, 'manifest.json')))
app.use('/logo', express.static(path.join(__dirname, 'logo')))
app.use('/service-worker.js', express.static(path.join(__dirname, 'dist/service-worker.js')))

 

缓存静态资源

首先是像 CSS、JS 那些静态资源,因为自己的博客里引用的脚本样式都是透过 hash
做持久化缓存,类似于:main.ac62dexx.js 那样,然后打开强缓存,那样下次用户下次再拜访我的网站的时候就毫无再行请求资源。直接从浏览器缓存中读取。对于那有些资源,service
worker 没必要再去处理,直接放行让它去读取浏览器缓存即可。

自家觉得一旦你的站点加载静态资源的时候我并未打开强缓存,并且你只想通过前端去贯彻缓存,而不须要后端在参加进行调整,这可以动用
service worker 来缓存静态资源,否则就有点画蛇添足了。

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开发者工具中的瑟维斯(Service)(Service)Worker部分提供了关于当前页面worker的音讯。其中会体现worker中生出的荒唐,仍可以强制刷新,也足以让浏览器进入离线形式。

Cache Storage
部分例举了近来所有曾经缓存的资源。你可以在缓存必要立异的时候点击refresh按钮。

(2)Service Worker安装和激活

登记完将来,瑟维斯Worker就会开展设置,这些时候会触发install事件,在install事件之中可以缓存一些资源,如下sw-3.js:

JavaScript

const CACHE_NAME = “fed-cache”; this.add伊夫(Eve)ntListener(“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控制台所示:

亚洲必赢官网 8

ServiceWorker的API基本上都是重临Promise对象避免堵塞,所以要用Promise的写法。上边在设置ServiceWorker的时候就把首页的哀告给缓存起来了。在瑟维斯(Service)(Service)Worker的周转条件之中它有一个caches的全局对象,这些是缓存的进口,还有一个常用的clients的大局对象,一个client对应一个标签页。

在瑟维斯(Service)Worker里面可以运用fetch等API,它和DOM是割裂的,没有windows/document对象,不能直接操作DOM,不可以直接和页面交互,在瑟维斯(Service)Worker里面不可以得知当前页面打开了、当前页面的url是怎么,因为一个ServiceWorker管理当前开拓的多少个标签页,可以经过clients知道所有页面的url。还有可以透过postMessage的格局和主页面相互传递信息和数据,进而做些控制。

install完之后,就会触发瑟维斯(Service)(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");
});

ServiceWorker激活之后就可以监听fetch事件了,大家期望每得到一个资源就把它缓存起来,就绝不像上一篇涉嫌的Manifest需求先生成一个列表。

你也许会问,当自家刷新页面的时候不是又再度注册安装和激活了一个ServiceWorker?即使又调了三遍注册,但并不会重复登记,它发现”sw-3.js”那么些曾经登记了,就不会再登记了,进而不会触发install和active事件,因为脚下ServiceWorker已经是active状态了。当要求更新瑟维斯(Service)Worker时,如变成”sw-4.js”,或者变更sw-3.js的公文内容,就会重新挂号,新的Service(Service)Worker会先install然后进入waiting状态,等到重启浏览器时,老的瑟维斯(Service)Worker就会被替换掉,新的ServiceWorker进入active状态,假诺不想等到再也启航浏览器可以像上边一样在install里面调skipWaiting:

JavaScript

this.skipWaiting();

1
this.skipWaiting();

缓存页面

缓存页面显然是必要的,那是最中央的一对,当您在离线的场所下加载页面会之后出现:

亚洲必赢官网 9

究其原因就是因为您在离线状态下不能加载页面,现在有了 service
worker,固然你在没网络的意况下,也得以加载此前缓存好的页面了。

第四步:成立可用的离线页面

离线页面可以是静态的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);
          });
      })
  });

(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(Service)Worker就安装好了,他刷新页面或者打开首个页面的时候就可见把请求的资源一一做缓存,包罗图形、CSS、JS等,只要缓存里有了随便用户在线或者离线都可以健康访问。那样大家本来会有一个问题,这些缓存空间到底有多大?上一篇大家关系Manifest也算是地方存储,PC端的Chrome是5Mb,其实这一个说法在新本子的Chrome已经不可信了,在Chrome
61版本可以观望地点存储的空间和采纳状态:

亚洲必赢官网 10

内部Cache Storage是指ServiceWorker和Manifest占用的上空大小和,上图可以见见总的空间尺寸是20GB,大概是unlimited,所以基本上不用担心缓存会不够用。

缓存后端接口数据

缓存接口数据是索要的,但也不是必须经过 service worker
来贯彻,前端存放数据的地方有不少,比如通过 localstorage,indexeddb
来展开仓储。那里自己也是经过 service worker
来促成缓存接口数据的,借使想透过其余形式来完结,只要求专注好 url
路径与数量对应的映照关系即可。

开发者工具

Chrome浏览器提供了一多级的工具来增援您来调节瑟维斯(Service)Worker,日志也会一向呈现在控制台上。

你最好使用匿超情势来进展付出工作,那样可以祛除缓存对开发的烦扰。

最后,Chrome的Lighthouse恢宏也得以为你的渐进式Web应用提供一些改进消息。

(4)cache html

上边第(3)步把图片、js、css缓存起来了,不过一旦把页面html也缓存了,例如把首页缓存了,就会有一个两难的问题——Service(Service)Worker是在页面注册的,可是现在赢得页面的时候是从缓存取的,每一回都是一律的,所以就造成力不从心创新ServiceWorker,如变成sw-5.js,可是PWA又要求大家能缓存页面html。那如何做吧?谷歌(Google)的开发者文档它只是提到会存在那几个问题,但并从未注脚怎么化解那些题材。那几个的题材的化解就须求大家要有一个编制能知晓html更新了,从而把缓存里的html给替换掉。

Manifest更新缓存的机制是去看Manifest的公文内容有没有发生变化,若是暴发变化了,则会去立异缓存,ServiceWorker也是按照sw.js的文书内容有没有发生变化,大家可以借鉴那几个思想,若是请求的是html并从缓存里取出来后,再发个请求获取一个文件看html更新时间是不是暴发变化,如若暴发变化了则证实暴发变更了,进而把缓存给删了。所以可以在服务端通过决定这么些文件从而去立异客户端的缓存。如下代码:

JavaScript

this.add伊夫ntListener(“fetch”, function(event) { event.respondWith(
caches.match(event.request).then(response => { // cache hit if
(response) { //假诺取的是html,则看发个请求看html是不是更新了 if
(response.headers.get(“Content-Type”).indexOf(“text/html”) >= 0) {
console.log(“update html”); let url = new URL(event.request.url);
util.updateHtmlPage(url, event.request.clone(), event.clientId); }
return response; } return util.fetchPut(event.request.clone()); }) );
});

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
this.addEventListener("fetch", function(event) {
 
    event.respondWith(
        caches.match(event.request).then(response => {
            // cache hit
            if (response) {
                //如果取的是html,则看发个请求看html是否更新了
                if (response.headers.get("Content-Type").indexOf("text/html") >= 0) {
                    console.log("update html");
                    let url = new URL(event.request.url);
                    util.updateHtmlPage(url, event.request.clone(), event.clientId);
                }
                return response;
            }
 
            return util.fetchPut(event.request.clone());
        })
    );
});

透过响应头header的content-type是或不是为text/html,假使是的话就去发个请求获取一个文本,根据那么些文件的内容决定是不是须求删除缓存,那么些立异的函数util.updateHtmlPage是这么完毕的:

JavaScript

let pageUpdateTime = { }; let util = { updateHtmlPage: function (url,
htmlRequest) { let pageName = util.getPageName(url); let jsonRequest =
new Request(“/html/service-worker/cache-json/” + pageName + “.sw.json”);
fetch(jsonRequest).then(response => { response.json().then(content
=> { if (pageUpdateTime[pageName] !== content.update提姆e) {
console.log(“update page html”); // 假使有更新则重复得到html
util.fetchPut(htmlRequest); pageUpdate提姆e[pageName] =
content.updateTime; } }); }); }, delCache: function (url) {
caches.open(CACHE_NAME).then(cache => { console.log(“delete cache “

  • url); cache.delete(url, {ignoreVary: true}); }); } };
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
let pageUpdateTime = {
 
};
let util = {
    updateHtmlPage: function (url, htmlRequest) {
        let pageName = util.getPageName(url);
        let jsonRequest = new Request("/html/service-worker/cache-json/" + pageName + ".sw.json");
        fetch(jsonRequest).then(response => {
            response.json().then(content => {
                if (pageUpdateTime[pageName] !== content.updateTime) {
                    console.log("update page html");
                    // 如果有更新则重新获取html
                    util.fetchPut(htmlRequest);
                    pageUpdateTime[pageName] = content.updateTime;
                }
            });
        });
    },
    delCache: function (url) {
        caches.open(CACHE_NAME).then(cache => {
            console.log("delete cache " + url);
            cache.delete(url, {ignoreVary: true});
        });
    }
};

代码先去得到一个json文件,一个页面会对应一个json文件,那么些json的情节是如此的:

JavaScript

{“updateTime”:”10/2/2017, 3:23:57 PM”,”resources”: {img: [], css:
[]}}

1
{"updateTime":"10/2/2017, 3:23:57 PM","resources": {img: [], css: []}}

个中紧要有一个update提姆(Tim)e的字段,若是地方内存没有那个页面的update提姆e的数量仍然是和流行update提姆(Tim)e不雷同,则再度去赢得
html,然后放到缓存里。接着须要通告页面线程数据爆发变化了,你刷新下页面吗。那样就不用等用户刷新页面才能见效了。所以当刷新完页面后用postMessage文告页面:

JavaScript

let util = { postMessage: async function (msg) { const allClients =
await clients.matchAll(); allClients.forEach(client =>
client.postMessage(msg)); } }; util.fetchPut(htmlRequest, false,
function() { util.postMessage({type: 1, desc: “html found updated”, url:
url.href}); });

1
2
3
4
5
6
7
8
9
let util = {
    postMessage: async function (msg) {
        const allClients = await clients.matchAll();
        allClients.forEach(client => client.postMessage(msg));
    }
};
util.fetchPut(htmlRequest, false, function() {
    util.postMessage({type: 1, desc: "html found updated", url: url.href});
});

并规定type: 1就象征那是一个翻新html的音信,然后在页面监听message事件:

JavaScript

if(“serviceWorker” in navigator) {
navigator.serviceWorker.addEventListener(“message”, function(event) {
let msg = event.data; if (msg.type === 1 && window.location.href ===
msg.url) { console.log(“recv from service worker”, event.data);
window.location.reload(); } }); }

1
2
3
4
5
6
7
8
9
if("serviceWorker" in navigator) {
    navigator.serviceWorker.addEventListener("message", function(event) {
        let msg = event.data;
        if (msg.type === 1 && window.location.href === msg.url) {
            console.log("recv from service worker", event.data);
            window.location.reload();
        }  
    });
}

下一场当大家须要更新html的时候就立异json文件,那样用户就能收看最新的页面了。或者是当用户重新启航浏览器的时候会招致ServiceWorker的运行内存都被清空了,即存储页面更新时间的变量被清空了,这么些时候也会另行请求页面。

亟待小心的是,要把那么些json文件的http
cache时间设置成0,那样浏览器就不会缓存了,如下nginx的配备:

JavaScript

location ~* .sw.json$ { expires 0; }

1
2
3
location ~* .sw.json$ {
    expires 0;
}

因为那些文件是内需实时获取的,不可能被缓存,firefox默许会缓存,Chrome不会,加上http缓存时间为0,firefox也不会缓存了。

再有一种更新是用户更新的,例如用户发布了评论,需求在页面公告service
worker把html缓存删了重复赢得,那是一个扭转的新闻布告:

JavaScript

if (“serviceWorker” in navigator) {
document.querySelector(“.comment-form”).addEventListener(“submit”,
function() { navigator.serviceWorker.controller.postMessage({ type: 1,
desc: “remove html cache”, url: window.location.href} ); } }); }

1
2
3
4
5
6
7
8
9
10
if ("serviceWorker" in navigator) {
    document.querySelector(".comment-form").addEventListener("submit", function() {
            navigator.serviceWorker.controller.postMessage({
                type: 1,
                desc: "remove html cache",
                url: window.location.href}
            );
        }
    });
}

Service Worker也监听message事件:

JavaScript

const messageProcess = { // 删除html index 1: function (url) {
util.delCache(url); } }; let util = { delCache: function (url) {
caches.open(CACHE_NAME).then(cache => { console.log(“delete cache “

  • url); cache.delete(url, {ignoreVary: true}); }); } };
    this.addEventListener(“message”, function(event) { let msg = event.data;
    console.log(msg); if (typeof messageProcess[msg.type] === “function”)
    { messageProcess[msg.type](msg.url); } });
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const messageProcess = {
    // 删除html index
    1: function (url) {
        util.delCache(url);
    }
};
 
let util = {
    delCache: function (url) {
        caches.open(CACHE_NAME).then(cache => {
            console.log("delete cache " + url);
            cache.delete(url, {ignoreVary: true});
        });
    }
};
 
this.addEventListener("message", function(event) {
    let msg = event.data;
    console.log(msg);
    if (typeof messageProcess[msg.type] === "function") {
        messageProcess[msg.type](msg.url);
    }
});

据悉不一样的新闻类型调不一致的回调函数,假使是1的话就是去除cache。用户发布完评论后会触发刷新页面,刷新的时候缓存已经被删了就会再也去伏乞了。

那样就解决了实时更新的题目。

缓存策略

众所周知了什么样资源须要被缓存后,接下去就要切磋缓存策略了。

渐进式Web应用的要领

渐进式Web应用是一种新的技艺,所以接纳的时候一定要小心。也就是说,渐进式Web应用可以让你的网站在多少个钟头内获得改良,并且在不协助渐进式Web应用的浏览器上也不会潜移默化网站的体现。

唯独大家必要考虑以下几点:

4. Http/Manifest/Service Worker三种cache的关系

要缓存可以使用二种手段,使用Http
Cache设置缓存时间,也得以用Manifest的Application Cache,还是可以用ServiceWorker缓存,假使三者都用上了会如何啊?

会以Service(Service) Worker为事先,因为ServiceWorker把请求拦截了,它首先做处理,即便它缓存库里一些话平素重回,没有的话正常请求,就相当于尚未瑟维斯(Service)(Service)Worker了,那个时候就到了Manifest层,Manifest缓存里即使有的话就取那几个缓存,即使没有的话就一定于尚未Manifest了,于是就会从Http缓存里取了,倘若Http缓存里也从没就会发请求去获得,服务端依据Http的etag或者Modified
提姆e可能会回去304 Not
Modified,否则正常重回200和数据内容。那就是整一个赢得的进程。

据此如若既用了Manifest又用ServiceWorker的话应该会招致同一个资源存了四遍。不过可以让扶助Service(Service)Worker的浏览器选取瑟维斯(Service) Worker,而不支持的施用Manifest.

页面缓存策略

因为是 React
单页同构应用,每便加载页面的时候数据都是动态的,所以我动用的是:

  1. 网络优先的措施,即优先得到网络上流行的资源。当网络请求战败的时候,再去赢得
    service worker 里以前缓存的资源
  2. 当网络加载成功之后,就立异 cache
    中对应的缓存资源,有限支撑下次每回加载页面,都是上次走访的摩登资源
  3. 假如找不到 service worker 中 url 对应的资源的时候,则去赢得 service
    worker 对应的 /index.html 默许首页

// sw.js self.add伊芙(Eve)ntListener(‘fetch’, (e) => {
console.log(‘现在正在呼吁:’ + e.request.url); const currentUrl =
e.request.url; // 匹配上页面路径 if (matchHtml(currentUrl)) { const
requestToCache = e.request.clone(); e.respondWith( // 加载网络上的资源
fetch(requestToCache).then((response) => { // 加载失败 if (!response
|| response.status !== 200) { throw Error(‘response error’); } //
加载成功,更新缓存 const responseToCache = response.clone();
caches.open(cacheName).then((cache) => { cache.put(requestToCache,
responseToCache); }); console.log(response); return response;
}).catch(function() { //
获取对应缓存中的数据,获取不到则败北到收获默许首页 return
caches.match(e.request).then((response) => { return response ||
caches.match(‘/index.html’); }); }) ); } });

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
// sw.js
self.addEventListener(‘fetch’, (e) => {
  console.log(‘现在正在请求:’ + e.request.url);
  const currentUrl = e.request.url;
  // 匹配上页面路径
  if (matchHtml(currentUrl)) {
    const requestToCache = e.request.clone();
    e.respondWith(
      // 加载网络上的资源
      fetch(requestToCache).then((response) => {
        // 加载失败
        if (!response || response.status !== 200) {
          throw Error(‘response error’);
        }
        // 加载成功,更新缓存
        const responseToCache = response.clone();
        caches.open(cacheName).then((cache) => {
          cache.put(requestToCache, responseToCache);
        });
        console.log(response);
        return response;
      }).catch(function() {
        // 获取对应缓存中的数据,获取不到则退化到获取默认首页
        return caches.match(e.request).then((response) => {
           return response || caches.match(‘/index.html’);
        });
      })
    );
  }
});

干什么存在命中穿梭缓存页面的动静?

  1. 先是须要肯定的是,用户在首先次加载你的站点的时候,加载页面后才会去启动
    sw,所以率先次加载不可以通过 fetch 事件去缓存页面
  2. 本人的博客是单页应用,可是用户并不一定会透过首页进入,有可能会由此任何页面路径进入到自家的网站,那就造成自己在
    install 事件中根本无法指定须要缓存那一个页面
  3. 说到底完成的效率是:用户率先次打开页面,立刻断掉网络,依旧可以离线访问我的站点

构成方面三点,我的不二法门是:第两次加载的时候会缓存 /index.html 这几个资源,并且缓存页面上的数量,若是用户及时离线加载的话,那时候并不曾缓存对应的门径,比如 /archives 资源访问不到,那再次回到 /index.html 走异步加载页面的逻辑。

在 install 事件缓存 /index.html,保险了 service worker
第五次加载的时候缓存默许页面,留下退路。

import constants from ‘./constants’; const cacheName =
constants.cacheName; const apiCacheName = constants.apiCacheName; const
cacheFileList = [‘/index.html’]; self.addEventListener(‘install’, (e)
=> { console.log(‘Service Worker 状态: install’); const
cacheOpenPromise = caches.open(cacheName).then((cache) => { return
cache.addAll(cacheFileList); }); e.waitUntil(cacheOpenPromise); });

1
2
3
4
5
6
7
8
9
10
11
12
import constants from ‘./constants’;
const cacheName = constants.cacheName;
const apiCacheName = constants.apiCacheName;
const cacheFileList = [‘/index.html’];
 
self.addEventListener(‘install’, (e) => {
  console.log(‘Service Worker 状态: install’);
  const cacheOpenPromise = caches.open(cacheName).then((cache) => {
    return cache.addAll(cacheFileList);
  });
  e.waitUntil(cacheOpenPromise);
});

在页面加载完后,在 React 组件中及时缓存数据:

// cache.js import constants from ‘../constants’; const apiCacheName =
constants.apiCacheName; export const saveAPIData = (url, data) => {
if (‘caches’ in window) { // 伪造 request/response 数据
caches.open(apiCacheName).then((cache) => { cache.put(url, new
Response(JSON.stringify(data), { status: 200 })); }); } }; // React 组件
import constants from ‘../constants’; export default class extends
PureComponent { componentDidMount() { const { state, data } =
this.props; // 异步加载数据 if (state === constants.INITIAL_STATE ||
state === constants.FAILURE_STATE) { this.props.fetchData(); } else {
// 服务端渲染成功,保存页面数据 saveAPIData(url, data); } } }

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
// cache.js
import constants from ‘../constants’;
const apiCacheName = constants.apiCacheName;
 
export const saveAPIData = (url, data) => {
  if (‘caches’ in window) {
    // 伪造 request/response 数据
    caches.open(apiCacheName).then((cache) => {
      cache.put(url, new Response(JSON.stringify(data), { status: 200 }));
    });
  }
};
 
// React 组件
import constants from ‘../constants’;
export default class extends PureComponent {
  componentDidMount() {
    const { state, data } = this.props;
    // 异步加载数据
    if (state === constants.INITIAL_STATE || state === constants.FAILURE_STATE) {
      this.props.fetchData();
    } else {
        // 服务端渲染成功,保存页面数据
      saveAPIData(url, data);
    }
  }
}

那般就确保了用户率先次加载页面,立即离线访问站点后,尽管无法像首次一样可以服务端渲染数据,可是之后能通过获取页面,异步加载数据的法门构建离线应用。

亚洲必赢官网 11

用户率先次访问站点,要是在不刷新页面的意况切换路由到其余页面,则会异步获取到的数据,当下次走访对应的路由的时候,则败北到异步获取数据。

亚洲必赢官网 12

当用户第二次加载页面的时候,因为 service worker
已经决定了站点,已经颇具了缓存页面的力量,之后在访问的页面都将会被缓存或者更新缓存,当用户离线访问的的时候,也能访问到服务端渲染的页面了。

亚洲必赢官网 13

URL隐藏

当您的选拔就是一个单URL的应用程序时(比如游戏),我提议你隐藏地址栏。除此之外的意况我并不提出你隐藏地址栏。在Manifest中,display: minimal-ui 或者 display: browser对于多数处境来说丰裕用了。

5. 施用Web App Manifest添加桌面入口

在意那里说的是另外一个Manifest,这一个Manifest是一个json文件,用来放网站icon名称等信息以便在桌面添加一个图标,以及成立一种打开那些网页就如打开App一样的法力。上边一向说的Manifest是被扬弃的Application
Cache的Manifest。

以此Maifest.json文件可以这么写:

JavaScript

{ “short_name”: “人人FED”, “name”: “人人网FED,专注于前者技术”,
“icons”: [ { “src”: “/html/app-manifest/logo_48.png”, “type”:
“image/png”, “sizes”: “48×48” }, { “src”:
“/html/app-manifest/logo_96.png”, “type”: “image/png”, “sizes”: “96×96”
}, { “src”: “/html/app-manifest/logo_192.png”, “type”: “image/png”,
“sizes”: “192×192” }, { “src”: “/html/app-manifest/logo_512.png”,
“type”: “image/png”, “sizes”: “512×512” } ], “start_url”:
“/?launcher=true”, “display”: “standalone”, “background_color”:
“#287fc5”, “theme_color”: “#fff” }

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
{
  "short_name": "人人FED",
  "name": "人人网FED,专注于前端技术",
  "icons": [
    {
      "src": "/html/app-manifest/logo_48.png",
      "type": "image/png",
      "sizes": "48×48"
    },
    {
      "src": "/html/app-manifest/logo_96.png",
      "type": "image/png",
      "sizes": "96×96"
    },
    {
      "src": "/html/app-manifest/logo_192.png",
      "type": "image/png",
      "sizes": "192×192"
    },
    {
      "src": "/html/app-manifest/logo_512.png",
      "type": "image/png",
      "sizes": "512×512"
    }
  ],
  "start_url": "/?launcher=true",
  "display": "standalone",
  "background_color": "#287fc5",
  "theme_color": "#fff"
}

icon须要预备多种条件,最大需求512px *
512px的,那样Chrome会自动去挑选合适的图形。就算把display改成standalone,从变化的图标打开就会像打开一个App一样,没有浏览器地址栏那一个东西了。start_url指定打开之后的进口链接。

下一场添加一个link标签指向这些manifest文件:

JavaScript

<link rel=”manifest” href=”/html/app-manifest/manifest.json”>

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

那般组合Service(Service) Worker缓存:
亚洲必赢官网 14把start_url指向的页面用瑟维斯(Service)Worker缓存起来,这样当用户用Chrome浏览器打开那个网页的时候,Chrome就会在底层弹一个提醒,询问用户是还是不是把那个网页添加到桌面,假使点“添加”就会变动一个桌面图标,从那几个图标点进去就像打开一个App一样。感受如下:

亚洲必赢官网 15

相比窘迫的是Manifest目前唯有Chrome帮忙,并且不得不在安卓系统上利用,IOS的浏览器无法添加一个桌面图标,因为IOS没有开放那种API,不过自己的Safari却又是可以的。

综上,本文介绍了怎么用瑟维斯(Service)(Service) Worker结合Manifest做一个PWA离线Web
APP,重若是用ServiceWorker控制缓存,由于是写JS,相比灵敏,还足以与页面举行通信,别的通过请求页面的创新时间来判定是或不是要求创新html缓存。瑟维斯(Service)(Service)Worker的包容性不是特地好,然则前景相比较光明,浏览器都在准备支持。现阶段可以组成offline
cache的Manifest做离线应用。

有关阅读:

  1. 何以要把网站升级到HTTPS
  2. 如何把网站升级到http/2
  3. 本身是怎么让网站用上HTML5
    Manifest

1 赞 1 收藏
评论

亚洲必赢官网 16

接口缓存策略

谈完页面缓存,再来讲讲接口缓存,接口缓存就跟页面缓存很接近了,唯一的不等在于:页面第一遍加载的时候不肯定有缓存,不过会有接口缓存的留存(因为伪造了
cache 中的数据),所以缓存策略跟页面缓存类似:

  1. 网络优先的法子,即优先获得网络上接口数据。当网络请求战败的时候,再去得到service worker 里往日缓存的接口数据
  2. 当网络加载成功之后,就立异 cache
    中对应的缓存接口数据,有限支撑下次每一趟加载页面,都是上次走访的风靡接口数据

所以代码就好像那样(代码类似,不再赘述):

self.add伊夫ntListener(‘fetch’, (e) => { console.log(‘现在正在呼吁:’

  • e.request.url); const currentUrl = e.request.url; if
    (matchHtml(currentUrl)) { // … } else if (matchApi(currentUrl)) {
    const requestToCache = e.request.clone(); e.respondWith(
    fetch(requestToCache).then((response) => { if (!response ||
    response.status !== 200) { return response; } const responseToCache =
    response.clone(); caches.open(apiCacheName).then((cache) => {
    cache.put(requestToCache, responseToCache); }); return response;
    }).catch(function() { return caches.match(e.request); }) ); } });
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
self.addEventListener(‘fetch’, (e) => {
  console.log(‘现在正在请求:’ + e.request.url);
  const currentUrl = e.request.url;
  if (matchHtml(currentUrl)) {
    // …
  } else if (matchApi(currentUrl)) {
    const requestToCache = e.request.clone();
    e.respondWith(
      fetch(requestToCache).then((response) => {
        if (!response || response.status !== 200) {
          return response;
        }
        const responseToCache = response.clone();
        caches.open(apiCacheName).then((cache) => {
          cache.put(requestToCache, responseToCache);
        });
        return response;
      }).catch(function() {
        return caches.match(e.request);
      })
    );
  }
});

此间其实可以再展开优化的,比如在获取数据接口的时候,可以先读取缓存中的接口数据举行渲染,当真正的网络接口数据重返之后再开展调换,那样也能使得削减用户的首屏渲染时间。当然那也许会发出页面闪烁的机能,可以加上一些卡通来展开衔接。

缓存过大

您不可以将你网站中的所有内容缓存下来。对于小部分的网站的话缓存所有内容并不是一个题目,不过倘诺一个网站包括了上千个页面吗?很显眼不是所有人对网站中的所有内容都感兴趣。存储是有限量的,如若您将富有访问过的页面都缓存下来的话,缓存大小会拉长额很快。

您可以如此制定你的缓存策略:

  • 只缓存首要的页面,比如主页,联系人页面和多年来浏览小说的页面。
  • 决不缓存任何图片,视频和大文件
  • 定时清理旧的缓存
  • 提供一个“离线阅读”按钮,这样用户就可以选拔要求缓存哪些内容了。

其他问题

到现行竣事,已经大概可以兑现 service worker
离线缓存应用的机能了,可是还有如故存在有的题目:

缓存刷新

演示代码中在提倡呼吁以前会先查询缓存。当用户处于离线状态时,那很好,可是要是用户处于在线状态,那他只会浏览到相比较老旧的页面。

种种资源比如图片和录像不会变动,所以一般都把那么些静态资源设置为长时间缓存。这几个资源可以直接缓存一年(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

默许情形下,页面的呼吁(fetch)不会通过 sw,除非它自身是经过 sw
获取的,也就是说,在设置 sw 之后,必要刷新页面才能有作用。sw
在安装成功并激活以前,不会响应 fetch或push等事件。

因为站点是单页面应用,那就造成了你在切换路由(没有刷新页面)的时候从不缓存接口数据,因为那时候
service worker 还不曾起来工作,所以在加载 service worker
的时候须求连忙地激活它。代码如下:

self.addEventListener(‘activate’, (e) => { console.log(‘Service
Worker 状态: activate’); const cachePromise = caches.keys().then((keys)
=> { return Promise.all(keys.map((key) => { if (key !== cacheName
&& key !== apiCacheName) { return caches.delete(key); } return null;
})); }); e.waitUntil(cachePromise); // 快捷激活 sw,使其能够响应 fetch
事件 return self.clients.claim(); });

1
2
3
4
5
6
7
8
9
10
11
12
13
14
self.addEventListener(‘activate’, (e) => {
  console.log(‘Service Worker 状态: activate’);
  const cachePromise = caches.keys().then((keys) => {
    return Promise.all(keys.map((key) => {
      if (key !== cacheName && key !== apiCacheName) {
        return caches.delete(key);
      }
      return null;
    }));
  });
  e.waitUntil(cachePromise);
  // 快速激活 sw,使其能够响应 fetch 事件
  return self.clients.claim();
});

一些作品说还亟需在 install
事件中添加 self.skipWaiting(); 来跳过等待时间,可是自己在实践中发现即便不添加也可以健康激活
service worker,原因未知,有读者知道的话可以调换下。

目前当你首先次加载页面,跳转路由,立时离线访问的页面,也可以顺遂地加载页面了。

小结

迄今截止,相信你一旦依据本文一步一步操作下来,你也足以急忙把温馨的Web应用转为PWA。在转为了PWA后,若是有应用满足PWA
模型的前端控件的需要,你可以试试纯前端表格控件SpreadJS,适用于
.NET、Java 和移动端等平台的表格控件一定不会令你失望的。

原文链接:

1 赞 1 收藏
评论

亚洲必赢官网 17

毫不强缓存 sw.js

用户每便访问页面的时候都会去重新得到sw.js,根据文件内容跟之前的本子是不是相同来判断 service worker
是不是有更新。所以借使您对 sw.js
开启强缓存的话,就将深陷死循环,因为每便页面获得到的 sw.js
都是平等,那样就不可能晋级你的 service worker。

此外对 sw.js 开启强缓存也是不曾必要的:

  1. 本人 sw.js
    文件本身就很小,浪费不了多少带宽,觉得浪费可以动用协议缓存,但附加扩张开销负担
  2. sw.js 是在页面空闲的时候才去加载的,并不会潜移默化用户首屏渲染速度

防止改变 sw 的 URL

在 sw 中那样做是“最差实践”,要在原地点上修修改改 sw。

举个例证来证实为啥:

  1. index.html 注册了 sw-v1.js 作为 sw
  2. sw-v1.js 对 index.html 做了缓存,也就是缓存优先(offline-first)
  3. 你更新了 index.html 重新登记了在新地方的 sw sw-v2.js

只要您像上边那么做,用户永远也拿不到 sw-v2.js,因为 index.html 在
sw-v1.js 缓存中,那样的话,即使您想翻新为 sw-v2.js,还须求改变原来的
sw-v1.js。

测试

将来,大家早就形成了运用 service worker
对页面举办离线缓存的效果,如若想感受效果的话,访问我的博客:

随意浏览任意的页面,然后关掉网络,再次访问,此前你浏览过的页面都得以在离线的动静下展开走访了。

IOS 需求 11.3 的版本才支撑,使用 Safari 进行访问,Android 请选拔协助service worker 的浏览器

manifest 桌面应用

面前讲完了什么样运用 service worker 来离线缓存你的同构应用,然则 PWA
不仅限于此,你还足以应用安装 manifest
文件来将你的站点添加到活动端的桌面上,从而已毕趋近于原生应用的经验。

亚洲必赢官网,使用 webpack-pwa-manifest 插件

本人的博客站点是因此 webpack 来构建前端代码的,所以自己在社区里找到
webpack-pwa-manifest 插件用来生成 manifest.json。

第一安装好 webpack-pwa-manifest 插件,然后在您的 webpack
配置文件中增进:

// webpack.config.prod.js const WebpackPwaManifest =
require(‘webpack-pwa-manifest’); module.exports =
webpackMerge(baseConfig, { plugins: [ new WebpackPwaManifest({ name:
‘Lindz\’s Blog’, short_name: ‘Blog’, description: ‘An isomorphic
progressive web blog built by React & Node’, background_color: ‘#333’,
theme_color: ‘#333’, filename: ‘manifest.[hash:8].json’, publicPath:
‘/’, icons: [ { src: path.resolve(constants.publicPath, ‘icon.png’),
sizes: [96, 128, 192, 256, 384, 512], // multiple sizes destination:
path.join(‘icons’) } ], ios: { ‘apple-mobile-web-app-title’: ‘Lindz\’s
Blog’, ‘apple-mobile-web-app-status-bar-style’: ‘#000’,
‘apple-mobile-web-app-capable’: ‘yes’, ‘apple-touch-icon’:
‘//xxx.com/icon.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
// webpack.config.prod.js
const WebpackPwaManifest = require(‘webpack-pwa-manifest’);
module.exports = webpackMerge(baseConfig, {
  plugins: [
    new WebpackPwaManifest({
      name: ‘Lindz\’s Blog’,
      short_name: ‘Blog’,
      description: ‘An isomorphic progressive web blog built by React & Node’,
      background_color: ‘#333’,
      theme_color: ‘#333’,
      filename: ‘manifest.[hash:8].json’,
      publicPath: ‘/’,
      icons: [
        {
          src: path.resolve(constants.publicPath, ‘icon.png’),
          sizes: [96, 128, 192, 256, 384, 512], // multiple sizes
          destination: path.join(‘icons’)
        }
      ],
      ios: {
        ‘apple-mobile-web-app-title’: ‘Lindz\’s Blog’,
        ‘apple-mobile-web-app-status-bar-style’: ‘#000’,
        ‘apple-mobile-web-app-capable’: ‘yes’,
        ‘apple-touch-icon’: ‘//xxx.com/icon.png’,
      },
    })
  ]
})

大概地演讲下安插音讯:

  1. name: 应用名称,就是图标上面的突显名称
  2. short_name: 应用名称,但 name 不可能展现完全时候则显示那一个
  3. background_color、theme_color:顾名思义,相应的水彩
  4. publicPath: 设置 cdn 路径,跟 webpack 里的 publicPath 一样
  5. icons: 设置图标,插件会自行帮你转移分裂 size
    的图形,可是图片大小必须超出最大 sizes
  6. ios: 设置在 safari 中哪些去添加桌面应用

安装完事后,webpack 会在构建进度中生成对应的 manifest 文件,并在 html
文件中引用,上边就是生成 manifest 文件:

{ “icons”: [ { “src”:
“/icons/icon_512x512.79ddc5874efb8b481d9a3d06133b6213.png”, “sizes”:
“512×512”, “type”: “image/png” }, { “src”:
“/icons/icon_384x384.09826bd1a5d143e05062571f0e0e86e7.png”, “sizes”:
“384×384”, “type”: “image/png” }, { “src”:
“/icons/icon_256x256.d641a3644ce20c06855db39cfb2f7b40.png”, “sizes”:
“256×256”, “type”: “image/png” }, { “src”:
“/icons/icon_192x192.8f11e077242cccd9c42c0cbbecd5149c.png”, “sizes”:
“192×192”, “type”: “image/png” }, { “src”:
“/icons/icon_128x128.cc0714ab18fa6ee6de42ef3d5ca8fd09.png”, “sizes”:
“128×128”, “type”: “image/png” }, { “src”:
“/icons/icon_96x96.dbfccb1a5cef8093a77c079f761b2d63.png”, “sizes”:
“96×96”, “type”: “image/png” } ], “name”: “Lindz’s Blog”,
“short_name”: “Blog”, “orientation”: “portrait”, “display”:
“standalone”, “start_url”: “.”, “description”: “An isomorphic
progressive web blog built by React & Node”, “background_color”:
“#333”, “theme_color”: “#333” }

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
{
  "icons": [
    {
      "src": "/icons/icon_512x512.79ddc5874efb8b481d9a3d06133b6213.png",
      "sizes": "512×512",
      "type": "image/png"
    },
    {
      "src": "/icons/icon_384x384.09826bd1a5d143e05062571f0e0e86e7.png",
      "sizes": "384×384",
      "type": "image/png"
    },
    {
      "src": "/icons/icon_256x256.d641a3644ce20c06855db39cfb2f7b40.png",
      "sizes": "256×256",
      "type": "image/png"
    },
    {
      "src": "/icons/icon_192x192.8f11e077242cccd9c42c0cbbecd5149c.png",
      "sizes": "192×192",
      "type": "image/png"
    },
    {
      "src": "/icons/icon_128x128.cc0714ab18fa6ee6de42ef3d5ca8fd09.png",
      "sizes": "128×128",
      "type": "image/png"
    },
    {
      "src": "/icons/icon_96x96.dbfccb1a5cef8093a77c079f761b2d63.png",
      "sizes": "96×96",
      "type": "image/png"
    }
  ],
  "name": "Lindz’s Blog",
  "short_name": "Blog",
  "orientation": "portrait",
  "display": "standalone",
  "start_url": ".",
  "description": "An isomorphic progressive web blog built by React & Node",
  "background_color": "#333",
  "theme_color": "#333"
}

html 中会引用那个文件,并且增进对 ios 添加桌面应用的帮助,就好像那样。

<!DOCTYPE html> <html lang=en> <head> <meta
name=apple-mobile-web-app-title content=”Lindz’s Blog”> <meta
name=apple-mobile-web-app-capable content=yes> <meta
name=apple-mobile-web-app-status-bar-style content=#838a88> <link
rel=apple-touch-icon href=xxxxx> <link rel=manifest
href=/manifest.21d63735.json> </head> </html>

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html lang=en>
<head>
  <meta name=apple-mobile-web-app-title content="Lindz’s Blog">
  <meta name=apple-mobile-web-app-capable content=yes>
  <meta name=apple-mobile-web-app-status-bar-style content=#838a88>
  <link rel=apple-touch-icon href=xxxxx>
  <link rel=manifest href=/manifest.21d63735.json>
</head>
</html>

就这么简单,你就足以选拔 webpack 来添加你的桌面应用了。

测试

添加完之后你可以由此 chrome 开发者工具 Application – Manifest 来查看你的
mainfest 文件是还是不是见效:

亚洲必赢官网 18

这样表明您的布局生效了,安卓机遇自动识别你的安排文件,并打听用户是或不是丰富。

结尾

讲到那大概就完了,等之后 IOS 支持 PWA
的其他功用的时候,到时候我也会相应地去实践其他 PWA 的表征的。现在 IOS
11.3 也仅仅协助 PWA 中的 service worker 和 app manifest
的作用,可是相信在不久的明日,其余的作用也会相应获得扶助,到时候相信 PWA
将会在运动端绽放异彩的。

1 赞 收藏
评论

亚洲必赢官网 19

网站地图xml地图