入门教程,离线网页应用

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做1个离线网页应用,结果被普遍网友吐槽说那一个事物已经被deprecated,移出web标准了,未来被ServiceWorker替代了,不管什么样,Manifest的有的思索如故得以借用的。小编又将网站升级到了ServiceWorker,倘使是用Chrome等浏览器就用ServiceWorker做离线缓存,如果是Safari浏览器就依旧用Manifest,读者可以打开这些网站感受一下,断网也是能健康打开。

前方的话

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

 

前言

近日在给作者的博客网站 PWA 升级,顺便就记录下 React 同构应用在动用 PWA
时际遇的题材,那里不会从头初步介绍怎么着是 PWA,倘使你想学习 PWA
相关文化,能够看下下边作者收藏的有的作品:

  • 您的首先个 Progressive Web
    App
  • 【ServiceWorker】生命周期那一个事儿
  • 【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 Worker是谷歌(谷歌)倡导的已毕PWA(Progressive Web
App)的1个最首要角色,PWA是为了缓解古板Web APP的毛病:

(1)没有桌面入口

(2)无法离线使用

(3)没有Push推送

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

皇家赌场手机版 1

ServiceWorker是在后台运转的一条服务Worker线程,上图我开了三个标签页,所以浮现了八个Client,可是不管开多少个页面都只有2个Worker在负责管理。这一个Worker的做事是把一些财富缓存起来,然后拦截页面的伸手,先看下缓存Curry有没有,假使某些话就从缓存里取,响应200,反之没有的话就走正规的乞请。具体来说,ServiceWorker结合Web App Manifest能落成以下工作(那也是PWA的检测标准):

皇家赌场手机版 2

回顾可以离线使用、断网时重临200、能唤起用户把网站添加2个图标到桌面上等。

作用演示

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

【添加到桌面】

皇家赌场手机版 3

【离线缓存】

   由于手机录屏采用不或者举行离线录像,改由模拟器模拟离线效果

皇家赌场手机版 4

 

PWA 特性

PWA 不是一味的某项技术,而是一堆技术的碰面,比如:ServiceWorker,manifest 添加到桌面,push、notification api 等。

而就在近日时间,IOS 11.3 刚刚协理 Service worker 和好像 manifest
添加到桌面的性情,所以本次 PWA
改造主要仍然兑现那两某个机能,至于其余的表征,等 iphone 帮助了再升级吗。

其次步:创制一个应用程序清单(Manifest)

应用程序清单提供了和当下渐进式Web应用的连锁新闻,如:

  • 应用程序名
  • 描述
  • 具备图片(包括主屏幕图标,运转显示屏页面和用的图片或然网页上用的图纸)

本质上讲,程序清单是页面上用到的图标和核心等财富的元数据。

程序清单是2个坐落您使用根目录的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: UCRUISERL范围。比如:即便你将“/app/”设置为U逍客L范围时,那一个动用就会直接在那一个目录中。
  • 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: 一个饱含全体图片的数组。该数组中种种成分包涵了图片的U奔驰G级L,大小和档次。

2. Service Worker的支撑情形

Service Worker近日只有Chrome/Firfox/Opera帮助:

皇家赌场手机版 5

Safari和艾德ge也在备选资助Service Worker,由于ServiceWorker是谷歌(谷歌(Google))大旨的一项正式,对于生态比较封闭的Safari来说也是迫于事势开头准备扶助了,在Safari
TP版本,可以看来:

皇家赌场手机版 6

在试验功能(Experimental Features)里曾经有ServiceWorker的菜单项了,只是尽管打开也是不只怕用,会提醒您还从未达成:

皇家赌场手机版 7

但无论如何,至少申明Safari已经准备扶助ServiceWorker了。此外还足以看出在今年二〇一七年一月发表的Safari
11.0.1本子现已支撑Web福睿斯TC了,所以Safari照旧1个上扬的子女。

艾德ge也准备资助,所以Service Worker的前景十一分美好。

概述

  PWA 的首要性特色包罗下边三点:

  一 、可看重 – 即便在不平稳的互连网环境下,也能弹指间加载并显现

  贰 、体验 – 飞快响应,并且有平整的动画片响应用户的操作

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

  主要效能包含站点可添加至主显示器、全屏格局运营、援救离线缓存、新闻推送等

【PRPL模式】

  “P凯雷德PL”(读作 “purple”)是 谷歌 的工程师指出的一种 web
应用架构情势,它目的在于利用现代 web 平台的新技巧以小幅优化移动 web
的性质与感受,对什么样社团与布署高质量的 PWA 系统提供了一种高层次的空洞

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

  ① 、PUSH/PRELOAD,推送/预加载伊始 U君越L 路由所需的严重性财富

  二 、RENDECRUISER,渲染先导路由,尽快让动用可被交互

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

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

【Service workers】

  Service Workers 是谷歌(谷歌) chrome 团队提出并大力推广的一项 web 技术。在
贰零壹肆 年,它加入到 W3C 标准,进入草案阶段

  PWA 的关键在于 Service Workers 。就其宗旨来说,Service Workers
只是后台启动的 worker 脚本。它们是用 JavaScript
编写的,只需短短几行代码,它们便可使开发者可以阻止网络请求,处理推送音信并进行许多任何任务

  瑟维斯 Worker 中用到的有的全局变量:

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

  Service Worker 的办事机制大概如下:用户访问3个独具 Service Worker
的页面,浏览器就会下载那个 Service Worker
并尝试安装、激活。一旦激活,Service Worker
就到后台开始工作。接下来用户访问那么些页面大概每隔一个时刻浏览器都会下载这么些Service Worker,纵然监测到 Service Worker 有更新,就会重新安装并激活新的
Service Worker,同时 revoke 掉旧的 Service Worker,那就是 SW 的生命周期

  因为 Service Worker 有着近期的权杖接触数据,因而 Service Worker
只可以被安装在 HTTPS 加密的页面中,即便无形当中提升了 PWA
的门路,可是也是为着安全做考虑

 

Service Worker

service worker
在我看来,类似于1个跑在浏览器后台的线程,页面第四回加载的时候会加载那些线程,在线程激活之后,通过对
fetch 事件,可以对各类拿到的资源拓展控制缓存等。

其三步:创立二个 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’);
}

即便您不须求离线的相干作用,您可以只成立1个 /service-worker.js文本,那样用户就足以直接设置您的Web应用了!

ServiceWorker这一个定义只怕相比较难懂,它事实上是多少个工作在其他线程中的标准的Worker,它不能够访问页面上的DOM成分,没有页面上的API,但是可以阻碍全体页面上的网络请求,包含页面导航,请求能源,Ajax请求。

下面就是使用全站HTTPS的显要缘由了。即便你没有在你的网站中运用HTTPS,贰个第叁方的台本就足以从其余的域名注入他协调的ServiceWorker,然后篡改全数的伸手——那的确是十分惊险的。

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

3. 使用Service Worker

ServiceWorker的应用套路是先登记三个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)。应用可以有五个缓存存储,可是在采纳时只会使用其中3个缓存存储。每当缓存存储有转变时,新的本子号将会钦定到缓存存储中。新的缓存存储将会作为当下的缓存存储,从前的缓存存储将会被作废。
  2. 二个离线的页面地址(offlineU大切诺基L):当用户访问了事先未曾访问过的地址时,该页面将会展现。
  3. 贰个包涵了具备必须文件的数组,包涵保持页面平日机能的CSS和JavaScript。在本示例中,作者还添加了主页和logo。当有两样的U奔驰G级L指向同3个能源时,你也可以将那么些U帕杰罗L分别写到那些数组中。offlineU途锐L将会投入到这么些数组中。
  4. 咱俩也可以将部分非必要的缓存文件(installFilesDesirable)。那几个文件在设置进度元帅会被下载,但如果下载失败,不会触发安装败北。

// 配置文件 const version = ‘1.0.0’, CACHE = version + ‘::PWAsite’,
offlineU奥德赛L = ‘/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);
    });
}

终极,大家抬高1个install的风浪监听器。waitUntil方法有限辅助了service
worker不会设置直到其休戚相关的代码被实施。那里它会举行installStaticFiles()方法,然后self.skipWaiting()艺术来激活service
worker:

// 应用设置 self.add伊芙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 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文件就是瑟维斯Worker的运营环境,如若无法得逞注册的话就会抛分外,如Safari
TP即使有其一目的,但是会抛很是无法使用,就可以在catch里面处理。那里有个难题是干什么须要在load事件运维呢?因为你要拾贰分运转多少个线程,运维将来你或许还会让它去加载财富,这几个都以必要占用CPU和带宽的,我们应当保障页面能健康加载完,然后再起步我们的后台线程,无法与平时的页面加载暴发竞争,这些在低端移动设备意义比较大。

再有一些索要注意的是ServiceWorker和Cookie一样是有Path路径的概念的,尽管您设定三个cookie假如叫time的path=/page/A,在/page/B那么些页面是不可见拿走到这么些cookie的,假使设置cookie的path为根目录/,则具有页面都能得到到。类似地,如果注册的时候使用的js路径为/page/sw.js,那么那些ServiceWorker只好管理/page路径下的页面和财富,而不能处理/api路径下的,所以一般把ServiceWorker注册到拔尖目录,如上面代码的”/sw-3.js”,那样这么些ServiceWorker就能接管页面的有所财富了。

充足到显示器

  没人愿意舍近求远地在运动设备键盘上输入长长的网址。通过添加到屏幕的成效,用户能够像从利用集团安装本机应用那样,选用为其设备增加1个神速链接,并且经过要顺遂得多

【配置项表明】

  使用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"
    }
  ]
}

【注意事项】

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

  ② 、图标的系列最好是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. 万一第1步失利,能源文件将会从网络中采取Fetch API来得到(和service
    worker中的fetch事件非亲非故)。获取到的财富将会投入到缓存中。
  3. 即使第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)); }); }) ); });

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文件。其余请求将会重返offlineUCR-VL 页面。

Chrome开发者工具中的ServiceWorker部分提供了关于当前页面worker的信息。其中会显得worker中发出的失实,仍是可以强制刷新,也得以让浏览器进入离线方式。

Cache Storage
部分例举了现阶段怀有曾经缓存的财富。你可以在缓存要求更新的时候点击refresh按钮。

(2)Service Worker安装和激活

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

JavaScript

const CACHE_NAME = “fed-cache”; this.add伊芙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

瑟维斯Worker的API基本上都以重回Promise对象幸免堵塞,所以要用Promise的写法。上边在设置ServiceWorker的时候就把首页的伏乞给缓存起来了。在ServiceWorker的周转条件之中它有二个caches的全局对象,这些是缓存的输入,还有1个常用的clients的全局对象,壹个client对应八个标签页。

在ServiceWorker里面可以应用fetch等API,它和DOM是割裂的,没有windows/document对象,不只怕直接操作DOM,无法直接和页面交互,在ServiceWorker里面不能得知当前页面打开了、当前页面的url是何等,因为2个ServiceWorker管理当前开拓的多少个标签页,可以由此clients知道全数页面的url。还有可以通过postMessage的方法和主页面互相传送音讯和数目,进而做些控制。

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

JavaScript

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

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

ServiceWorker激活之后就可见监听fetch事件了,大家愿意每得到3个财富就把它缓存起来,就无须像上一篇涉嫌的Manifest要求先生成1个列表。

你大概会问,当自个儿刷新页面的时候不是又重新挂号安装和激活了一个ServiceWorker?尽管又调了两次注册,但并不会再度挂号,它发现”sw-3.js”这些已经注册了,就不会再登记了,进而不会触发install和active事件,因为脚下ServiceWorker已经是active状态了。当必要更新ServiceWorker时,如变成”sw-4.js”,恐怕变更sw-3.js的公文内容,就会重新挂号,新的ServiceWorker会先install然后进入waiting状态,等到重启浏览器时,老的ServiceWorker就会被替换掉,新的ServiceWorker进入active状态,假使不想等到再也开动浏览器可以像上边一样在install里面调skipWaiting:

JavaScript

this.skipWaiting();

1
this.skipWaiting();

缓存页面

缓存页面分明是少不了的,那是最基本的一部分,当您在离线的场地下加载页面会之后出现:

皇家赌场手机版 9

究其原因就是因为您在离线状态下不只怕加载页面,今后有了 service
worker,即便你在没网络的情状下,也得以加载在此以前缓存好的页面了。

第4步:创造可用的离线页面

离线页面可以是静态的HTML,一般用来提醒用户眼下呼吁的页面权且不只怕使用。不过,大家得以提供一些得以翻阅的页面链接。

Cache
API可以在main.js中利用。然则,该API使用Promise,在不协理Promise的浏览器中会战败,全体的JavaScript执行会因而备受震慑。为了幸免那种气象,在访问/js/offlinepage.js的时候大家添加了一段代码来检查当前是还是不是在离线环境中:

/js/offlinepage.js 中以版本号为名称保存了近来的缓存,获取具有UTucsonL,删除不是页面的ULANDL,将这么些U福睿斯L排序然后将具有缓存的UOdysseyL显示在页面上:

// 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都一律才是如出一辙的能源,可以设定第2个参数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,若是跨域的能源帮助COEscortS,那么可以把request的mod改成cors。即使请求战败了,如404要么是过期等等的,那么也一向回到response让主页面处理,否则的话表明加载成功,把那几个response克隆三个放到cache里面,然后再重临response给主页面线程。注意能舒缓存里的能源一般只好是GET,通过POST获取的是不或然缓存的,所以要做个判断(当然你也得以手动把request对象的method改成get),还有把有个别民用不期待缓存的能源也做个判断。

如此那般假若用户打开过三遍页面,ServiceWorker就安装好了,他刷新页面大概打开第一个页面的时候就可以把请求的能源一一做缓存,包罗图片、CSS、JS等,只要缓存里有了不管用户在线恐怕离线都可以健康访问。那样我们自然会有一个标题,那一个缓存空间到底有多大?上一篇我们关系Manifest也终归地点存储,PC端的Chrome是5Mb,其实这么些说法在新本子的Chrome已经不规范了,在Chrome
61版本可以看看地点存储的空间和应用状态:

皇家赌场手机版 10

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

缓存后端接口数据

缓存接口数据是内需的,但也不是必须通过 service worker
来落成,前端存放数据的地点有过多,比如通过 localstorage,indexeddb
来开展仓储。那里本人也是因此 service worker
来落实缓存接口数据的,要是想经过其余措施来达成,只须求留意好 url
路径与数量对应的照射关系即可。

开发者工具

Chrome浏览器提供了一名目繁多的工具来协助您来调节ServiceWorker,日志也会直接显示在控制台上。

您最好使用匿有名的模特式来展用度付工作,那样可以去掉缓存对开发的困扰。

最后,Chrome的Lighthouse推而广之也足以为您的渐进式Web应用提供部分革新信息。

(4)cache html

地点第(3)步把图片、js、css缓存起来了,不过一旦把页面html也缓存了,例如把首页缓存了,就会有多个啼笑皆非的难点——ServiceWorker是在页面注册的,然而以后取得页面的时候是从缓存取的,每一遍都以同一的,所以就造成不能立异ServiceWorker,如变成sw-5.js,然则PWA又必要大家能缓存页面html。那如何做吧?谷歌(谷歌(Google))的开发者文档它只是提到会存在那一个题材,但并不曾认证怎么化解那些难题。这么些的难题的缓解就须要大家要有1个编制能精通html更新了,从而把缓存里的html给替换掉。

Manifest更新缓存的体制是去看Manifest的文件内容有没有发生变化,就算发生变化了,则会去创新缓存,ServiceWorker也是依照sw.js的文本内容有没有暴发变化,大家得以借鉴这几个思想,假如请求的是html并从缓存里取出来后,再发个请求获取1个文本看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 URAV4L(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.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}); }); } };
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文件,一个页面会对应3个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: []}}

里面主要有3个updateTime的字段,倘诺地点内存没有这一个页面的updateTime的数据依然是和新颖updateTime不均等,则再次去取得
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缓存删了再度得到,那是3个转头的音信文告:

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缓存,要是三者都用上了会怎么着啊?

会以瑟维斯 Worker为预先,因为ServiceWorker把请求拦截了,它开首做处理,如若它缓存Curry有的话从来再次来到,没有的话符合规律请求,就一定于尚未ServiceWorker了,这些时候就到了Manifest层,Manifest缓存里假使有些话就取那么些缓存,即便没有的话就一定于尚未Manifest了,于是就会从Http缓存里取了,若是Http缓存里也没有就会发请求去得到,服务端依据Http的etag或然Modified
Time只怕会回到304 Not
Modified,否则平常重回200和数目内容。那就是整三个收获的经过。

据此一旦既用了Manifest又用ServiceWorker的话应该会造成同一个能源存了三次。然而可以让协理ServiceWorker的浏览器采取Service Worker,而不支持的行使Manifest.

页面缓存策略

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

  1. 网络优先的不二法门,即优先得到网络上流行的能源。当互连网请求战败的时候,再去赢得
    service worker 里从前缓存的能源
  2. 当互联网加载成功以后,就立异 cache
    中对应的缓存财富,保险下次每一遍加载页面,都是上次作客的流行财富
  3. 即使找不到 service worker 中 url 对应的财富的时候,则去拿到 service
    worker 对应的 /index.html 暗许首页

// sw.js self.add伊芙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
第5遍加载的时候缓存专擅认同页面,留下退路。

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隐藏

当您的使用就是三个单U帕杰罗L的应用程序时(比如游戏),小编指出你隐藏地址栏。除此之外的情景本人并不提出您隐藏地址栏。在Manifest中,display: minimal-ui 或者 display: browser对于绝大部分动静来说丰硕用了。

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

瞩目那里说的是其余二个Manifest,那些Manifest是二个json文件,用来放网站icon名称等新闻以便在桌面添加1个图标,以及成立一种打开这一个网页就像是打开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,从变化的图标打开就会像打开3个App一样,没有浏览器地址栏这一个东西了。start_url钦点打开今后的入口链接。

接下来添加1个link标签指向这一个manifest文件:

JavaScript

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

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

那样组合Service Worker缓存:
皇家赌场手机版 14把start_url指向的页面用ServiceWorker缓存起来,那样当用户用Chrome浏览器打开那一个网页的时候,Chrome就会在底层弹三个提示,询问用户是或不是把那几个网页添加到桌面,若是点“添加”就会扭转三个桌面图标,从这几个图标点进去就如打开三个App一样。感受如下:

皇家赌场手机版 15

正如为难的是Manifest近来唯有Chrome协助,并且不得不在安卓系统上接纳,IOS的浏览器不可能添加二个桌面图标,因为IOS没有开放那种API,可是自身的Safari却又是可以的。

综上,本文介绍了怎么用Service Worker结合Manifest做2个PWA离线Web
APP,紧如果用ServiceWorker控制缓存,由于是写JS,比较灵活,还足以与页面进行通讯,此外通过请求页面的更新时间来判定是不是必要创新html缓存。瑟维斯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);
      })
    );
  }
});

那边其实可以再举办优化的,比如在获取数据接口的时候,可以先读取缓存中的接口数据开展渲染,当真正的互连网接口数据再次来到之后再举行轮换,那样也能使得压缩用户的首屏渲染时间。当然那可能会时有发生页面闪烁的成效,可以加上一些卡通来进行过渡。

缓存过大

你不可以将你网站中的全数情节缓存下来。对于小一些的网站以来缓存全数情节并不是1个题材,不过如果二个网站包括了上千个页面吗?很扎眼不是全体人对网站中的全部内容都感兴趣。存储是有限量的,即使您将全部访问过的页面都缓存下来的话,缓存大小会增加额很快。

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

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

其他难点

到现行得了,已经大致可以已毕 service worker
离线缓存应用的意义了,可是还有照旧存在有的标题:

缓存刷新

示范代码中在发起呼吁此前会先查询缓存。当用户处于离线状态时,那很好,可是只要用户处于在线状态,那她只会浏览到比较老旧的页面。

各个能源比如图片和摄像不会改变,所以一般都把那一个静态财富设置为深入缓存。这么些能源可以直接缓存一年(31,536,000秒)。在HTTP
Header中,就是:

Cache-Control: max-age=31536000

1
Cache-Control: max-age=31536000

页面,CSS和本子文件可能变化的更频仍一些,所以你可以设置1个比较小的缓存超时时间(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 的 UQX56L

在 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

Leave a Comment.