澳门新萄京官方网站-www.8455.com-澳门新萄京赌场网址

澳门新萄京官方网站:同构应用,离线网页应用

2019-05-06 作者:澳门新萄京赌场网址   |   浏览(66)

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博客   

在上一篇《自身是怎么着让网址用上HTML伍Manifest》介绍了怎么用Manifest做1个离线网页应用,结果被周围网民嘲弄说这几个事物已经被deprecated,移出web规范了,现在被ServiceWorker代替了,不管怎么着,Manifest的局地想想还是可以够借用的。作者又将网址晋级到了ServiceWorker,即便是用Chrome等浏览器就用ServiceWorker做离线缓存,要是是Safari浏览器就像故用Manifest,读者能够展开这几个网址感受一下,断网也是能健康展开。

关于PWA

PWA(Progressive Web App), 即渐进式web应用。PWA本质上是web应用,目标是通过多项新才干,在平安、品质、体验等方面给用户原生应用的心得。而且不必要像原生应用那样繁琐的下载、安装、进级等操作。

那边解释下概念中的“渐进式”,意思是以此web应用还在相连地发展中。因为近期来说,PWA还尚未成熟到一蹴即至的品位,想在平安、品质、体验上到达原生应用的法力仍然有那个的进级空间的。一方面是创设PWA的基金难题,另1方面是日前各大浏览器、安卓和IOS系统对此PWA的支撑和包容性还有待拉长。

本文作者将从网址缓存、http请求拦截、推送到主屏等效果,结合实例操作,一步步引领大家连忙玩转PWA才干。

目前的话

  渐进式互联网应用 ( 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网址在谷歌上的排行也会更靠前。

出于Chrome浏览器会私下认可将localhost以及127.x.x.x地址视为测试地方,所以在本示例中你并没有供给开启HTTPS。其余,出于调节和测试目标,您能够在开发银行Chrome浏览器的时候使用以下参数来关闭其对网址HTTPS的自己研讨:

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

1. 什么是Service Worker

Service Worker是谷歌(谷歌(Google))倡导的得以达成PWA(Progressive Web App)的多个重中之重剧中人物,PWA是为着解决守旧Web APP的缺陷:

(一)未有桌面入口

(二)不可能离线使用

(3)没有Push推送

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

澳门新萄京官方网站 1

ServiceWorker是在后台运转的一条服务Worker线程,上海体育场所小编开了五个标签页,所以展现了八个Client,可是不管开多少个页面都唯有贰个Worker在肩负管理。这些Worker的办事是把有个别能源缓存起来,然后拦截页面包车型客车请求,先看下缓存Curry有没有,假使局部话就从缓存里取,响应200,反之未有的话就走经常的恳求。具体来讲,ServiceWorker结合Web App Manifest能成功以下职业(那也是PWA的检查评定职业):

澳门新萄京官方网站 2

包蕴能够离线使用、断网时回来200、能唤起用户把网址增加二个图标到桌面上等。

Service Worker

ServiceWorker是PWA的主题技能,它可感到web应用提供离线缓存作用,当然不止如此,上面罗列了部分瑟维斯Worker的表征:

依照HTTPS 意况,那是创设PWA的硬性前提。(LAMP碰到网址晋级HTTPS化解方案)

是一个单身的 worker 线程,独立于当下网页进度,有谈得来独立的 worker context

可阻拦HTTP请求和响应,可缓存文件,缓存的文本能够在互联网离线状态时取到

能向客户端推送新闻

不能够直接操作 DOM

异步实现,内部大都以经过 Promise 落成

效能演示

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

【加多到桌面】

澳门新萄京官方网站 3

【离线缓存】

   由于手提式有线电话机录屏接纳不也许开展离线录制,改由模拟器模拟离线效果

澳门新萄京官方网站 4

 

PWA 特性

PWA 不是但是的某项本领,而是一群本事的汇聚,举例:ServiceWorker,manifest 增多到桌面,push、notification api 等。

而就在不久前时刻,IOS 1一.三 刚刚支持 Service worker 和周边 manifest 增添到桌面的特征,所以此番 PWA 退换重视还是促成那两部分作用,至于另外的特点,等 iphone 帮忙了再升格吗。

第一步:创设2个应用程序清单(Manifest)

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

  • 应用程序名
  • 描述
  • 全部图片(包涵主荧屏图标,运行显示器页面和用的图纸大概网页上用的图形)

精神上讲,程序清单是页面上用到的Logo和核心等财富的元数据。

程序清单是三个位于您使用根目录的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" : "72x72", "type" : "image/png" }, { "src" : "/images/logo/logo152.png", "sizes" : "152x152", "type" : "image/png" }, { "src" : "/images/logo/logo192.png", "sizes" : "192x192", "type" : "image/png" }, { "src" : "/images/logo/logo256.png", "sizes" : "256x256", "type" : "image/png" }, { "src" : "/images/logo/logo512.png", "sizes" : "512x512", "type" : "image/png" } ] }

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

程序清单文件创设完事后,你要求在各个页面上引用该公文:

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

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

以下属性在程序清单中时常利用,介绍表达如下:

  • name: 用户观看的施用名称
  • short_name: 应用短名称。当彰显选择名称的地点不够时,将选用该名称。
  • description: 应用描述。
  • start_url: 选拔起头路线,相对路线,默以为/。
  • scope: U奔驰G级L范围。举个例子:借使您将“/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冠道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了。其它还足以见到在当年20壹柒年2月发表的Safari 11.0.1版本已经辅助WebRTC了,所以Safari照旧2个更上一层楼的男女。

艾德ge也希图辅助,所以Service Worker的前景10分美好。

Service Worker生命周期

serviceworker的选取流程能够回顾总结为注册--安装--激活。

挂号其实便是告诉浏览器serviceworkerJS文件存放在什么职位,然后浏览器下载、解析、实施该文件,进而运营安装。这里我创制1个app.js文件,注册代码如下,将该文件在网址的head标签里引进。

if ('serviceWorker' in navigator) {

window.addEventListener('load', function () {

navigator.serviceWorker.register

.then(function (registration) {

// 注册成功

console.log('ServiceWorker registration successful with scope: ', registration.scope);

})

.catch(function {

// 注册失败:(

console.log('ServiceWorker registration failed: ', err);

});

});

}

当试行serviceworkerJS文件时,首先接触的是install事件,举办设置。安装的长河正是将点名的有的静态财富开始展览离线缓存。上面是本人的sw.js文件中的安装代码:

var CACHE_VERSION = 'sw_v8';

var CACHE_FILES = [

'/js/jquery/min.js',

'/js/zui/min.js',

'/js/chanzhi.js',

];

self.addEventListener('install', function {

event.waitUntil(

caches.open(CACHE_VERSION)

.then(cache => cache.addAll(CACHE_FILES)

));

});

当安装成功后,serviceworker就能够激活,那时就能够管理 activate 事件回调 (提供了翻新缓存计策的机遇)。并得以管理功用性的事件 fetch 、sync 、push 。

self.addEventListener('activate', function {

event.waitUntil(

caches.keys().then(function{

return Promise.all(keys.map(function{

if(key !== CACHE_VERSION){

return caches.delete;

}

}));

})

);

});

概述

  PWA 的重要特征包含上面3点:

  一、可信 - 纵然在不平静的互连网意况下,也能弹指间加载并显现

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

  三、粘性 - 像道具上的原生应用,具备沉浸式的用户体验,用户能够拉长到桌面

  主要功能包罗站点可增加至主荧屏、全屏方式运维、协理离线缓存、音讯推送等

【PRPL模式】

  “PRPL”(读作 “purple”)是 谷歌 的程序员建议的壹种 web 应用架构方式,它意在利用今世 web 平台的新技巧以小幅优化移动 web 的性质与体验,对怎么着组织与统一计划高质量的 PWA 系统提供了一种高等级次序的虚幻

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

  一、PUSH/PRELOAD,推送/预加载最先 UHighlanderL 路由所需的显要能源

  二、RENDE凯雷德,渲染起头路由,尽快让动用可被交互

  三、PRE-CACHE,用 瑟维斯 Worker 预缓存剩下的路由

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

【Service workers】

  Service Workers 是谷歌 chrome 团队提议并大力推广的1项 web 才具。在 20一伍 年,它加入到 W3C 标准,进入草案阶段

  PWA 的关键在于 Service Workers 。就其宗旨来讲,Service Workers 只是后台运转的 worker 脚本。它们是用 JavaScript 编写的,只需短短几行代码,它们便可使开荒者能够拦截互联网请求,管理推送新闻并实践诸多任何任务

  Service Worker 中用到的壹对全局变量:

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

  Service Worker 的工作体制大约如下:用户访问三个颇具 Service Worker 的页面,浏览器就能够下载那些 Service Worker 并尝试安装、激活。1旦激活,Service Worker 就到后台起头职业。接下来用户访问这么些页面恐怕每隔三个辰光浏览器都会下载这么些Service Worker,假若监测到 瑟维斯 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');
}

借使您无需离线的有关功用,您能够只创建二个 /service-worker.js文本,那样用户就能够直接设置您的Web应用了!

ServiceWorker那几个定义或许相比较难懂,它事实上是二个做事在别的线程中的标准的Worker,它不可以访问页面上的DOM成分,未有页面上的API,但是能够阻碍全体页面上的互连网请求,包蕴页面导航,请求财富,Ajax请求。

上边正是使用全站HTTPS的主要缘由了。即令你未有在您的网站中动用HTTPS,一个第一方的脚本就足以从任何的域名注入他本身的ServiceWorker,然后篡改全体的伸手——那确实是拾叁分危险的。

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

3. 使用Service Worker

ServiceWorker的利用套路是先注册2个Worker,然后后台就能够运行一条线程,能够在那条线程运转的时候去加载一些能源缓存起来,然后监听fetch事件,在那几个事件里拦截页面包车型地铁呼吁,先看下缓存里有未有,纵然有一向回到,否则不荒谬加载。或然是一开头不缓存,每一种能源请求后再拷贝壹份缓存起来,然后下2次呼吁的时候缓存里就有了。

serviceworker的缓存作用

设置时,serviceworker将大家钦赐的静态能源拓展缓存,你恐怕会问,假诺是实时的动态内容如何做,我们不容许预先将有所的文件财富提前线指挥部定好,让serviceworker缓存。那就要涉及serviceworker的掣肘HTTP请求响应的表征了。

serviceworker拦截http请求,首先去检查请求的能源在缓存中是不是留存,假使存在,则直接从缓存中调用,而且正是是无网络状态下,serviceworker也能调用缓存中的财富。如若缓存中从不请求的能源,则经过互联网去服务器上寻找,而且在找到并响应时,serviceworker会将其存入缓存中,以备下次再请求时直接从缓存中调用。

澳门新萄京官方网站 8serviceworker缓存流程

serviceworker文件中fetch事件代码如下:

self.addEventListener('fetch', function {

event.respondWith(

caches.match(event.request).then(function{

if{

return response;

}

var requestToCache = event.request.clone();

return fetch(requestToCache).then(

function{

if(!response || response.status !== 200){

return response;

}

var responseToCache = response.clone();

caches.open(CACHE_VERSION)

.then(function{

cache.put(requestToCache, responseToCache);

});

return response;

}

);

})

);

});

在网址前台通过浏览器开荒者工具,大家得以看下从缓存中调用能源的效应:

澳门新萄京官方网站 9serviceworker缓存调用澳门新萄京官方网站 10缓存文件

自己那边操作演示用的是谷歌浏览器,下边是各大浏览器对于serviceworker的帮助情状:

澳门新萄京官方网站 11serviceworker浏览器支持意况

离线缓存

  上面来经过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. 八个离线的页面地址(offlineUOdysseyL):当用户访问了事先未有访问过的地址时,该页面将会来得。
  3. 2个富含了装有必须文件的数组,包罗保持页面符合规律机能的CSS和JavaScript。在本示例中,笔者还增加了主页和logo。当有不一致的UCRUISERL指向同一个财富时,你也得以将这些U凯雷德L分别写到那些数组中。offlineULANDL将会参与到那些数组中。
  4. 咱俩也得以将部分非须要的缓存文件(installFilesDesirable)。那些文件在设置进度少将会被下载,但只要下载退步,不会接触安装失利。

// 配置文件 const version = '一.0.0', CACHE = version '::PWAsite', offlineU奥迪Q3L = '/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);
    });
}

终极,大家增添3个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)注册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完之后注册,注册的时候传3个js文件给它,这么些js文件正是ServiceWorker的运作条件,假若不可能得逞注册的话就能抛格外,如Safari TP尽管有那些目的,不过会抛分外不可能利用,就足以在catch里面管理。这里有个难题是为何需求在load事件运维呢?因为您要格外运维二个线程,运转现在你只怕还会让它去加载能源,那几个都以急需占用CPU和带宽的,大家应有保障页面能平时加载完,然后再起步我们的后台线程,不能与常规的页面加载发生竞争,这些在低等移动设备意义十分的大。

再有一些亟待小心的是ServiceWorker和Cookie同样是有帕特h路线的概念的,假设您设定1个cookie假若叫time的path=/page/A,在/page/B这些页面是无法获取到那些cookie的,假使设置cookie的path为根目录/,则装有页面都能博取到。类似地,倘使注册的时候利用的js路线为/page/sw.js,那么那几个ServiceWorker只可以管理/page路线下的页面和能源,而不可见管理/api路线下的,所以一般把ServiceWorker注册到拔尖目录,如下面代码的”/sw-叁.js”,那样那一个ServiceWorker就能接管页面包车型地铁有所能源了。

增加到主屏

PWA支持将web应用在主屏桌面上加多三个火速格局,以利于用户快捷访问,同时升级web应用重复访问的概率。你可能会说,今后运动端上的浏览器成效列表里一般都提供了“加多到桌面”的效劳呀,可是PWA与浏览器自带的丰硕到桌面包车型地铁兑现方式各异。

PWA无需用户刻意去功效列表中采取这一个成效按键,而是在用户访问web应用时,直接在分界面中提示2个加上到主屏桌面包车型大巴横幅,从web应用角度来讲,那事实上正是积极与被动的界别。

PWA实现该意义非凡轻便,只须求2个manifest.json文件,文件中用户能够自定义应用的起步页面、模板颜色、Logo等内容。上面是本身的manifest.json文件设置,大家可作参考:

{

"short_name": "蝉知财富站",

"name": "蝉知能源站",

"icons": [

{

"src": "chanzhiicon.png",

"type": "image/png",

"sizes": "48x48"

},

{

"src": "192.png",

"type": "image/png",

"sizes": "192x192"

},

{

"src": "512.png",

"type": "image/png",

"sizes": "512x512"

},

{

"src": "144.png",

"type": "image/png",

"sizes": "144x144"

}

],

"start_url": "/index.html",

"display": "standalone",

"background_color": "#2196F3",

"orientation": "landscape",

"theme_color": "#2196F3"

}

必要提示的是,方今活动端IOS系统的协理并倒霉,安卓手机上须利用伍柒本子以上的谷歌(谷歌(Google))浏览器能够支撑该意义,上边是自己在安卓手提式有线电电话机上的操作演示:

澳门新萄京官方网站 12充足到主屏

累加到显示器

  没人愿意神经过敏地在活动道具键盘上输入长长的网站。通过丰硕到显示屏的成效,用户可以像从利用集团安装本机应用那样,选取为其道具增加3个火速链接,并且经过要顺遂得多

【配置项表达】

  使用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 字段使用

  二、Logo的类型最棒是png,,且存在14四px的尺码,否则会收获如下提示

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. 设若第二步和第二步均失利,将会从缓存中回到精确的财富文件。

// 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文件。其余请求将会回去 offlineUOdysseyL 页面。

Chrome开垦者工具中的ServiceWorker部分提供了关于当前页面worker的音讯。当中会议及展览示worker中生出的错误,还能强制刷新,也得以让浏览器进入离线形式。

Cache Storage 部分例举了脚下怀有曾经缓存的能源。你能够在缓存必要更新的时候点击refresh开关。

(贰)瑟维斯 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"); // 创造和开采2个缓存库 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调节台所示:

澳门新萄京官方网站 13

ServiceWorker的API基本上都以再次回到Promise对象幸免堵塞,所以要用Promise的写法。上边在安装瑟维斯Worker的时候就把首页的伸手给缓存起来了。在ServiceWorker的运作意况之中它有1个caches的大局对象,那些是缓存的输入,还有二个常用的clients的全局对象,三个client对应一个标签页。

在ServiceWorker里面能够使用fetch等API,它和DOM是割裂的,没有windows/document对象,不能直接操作DOM,无法直接和页面交互,在ServiceWorker里面不能得知当前页面展开了、当前页面包车型地铁url是怎样,因为二个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事件了,大家希望每获得三个能源就把它缓存起来,就无须像上一篇涉嫌的Manifest供给先生成三个列表。

你恐怕会问,当本人刷新页面包车型客车时候不是又重新挂号安装和激活了1个ServiceWorker?即使又调了3遍注册,但并不会再一次注册,它开采”sw-三.js”这一个早已登记了,就不会再登记了,进而不会触发install和active事件,因为近年来ServiceWorker已经是active状态了。当需求更新瑟维斯Worker时,如产生”sw-四.js”,可能转移sw-叁.js的文书内容,就能够再一次登记,新的ServiceWorker会先install然后进入waiting状态,等到重启浏览器时,老的ServiceWorker就能够被替换掉,新的ServiceWorker进入active状态,固然不想等到再也开动浏览器能够像上边同样在install里面调skipWaiting:

JavaScript

this.skipWaiting();

1
this.skipWaiting();

小结

PWA本事正被广大公司及开垦者们关心,即使日前各配备的帮衬合营有待拉长,但这么些都正在不断的改革发展。小编也信任在不久的以往,PWA本通晓慢慢广泛推广,为常见集团和用户带来有利。本文和我们一起享受了PWA的有关才具与实例操作,后边还会就消息推送、数据同步等功效做进一步追究交换。假设大家在念书PWA的长河中相遇别的标题,招待一齐研讨调换。

缓存页面

缓存页面分明是必备的,那是最基本的部分,当你在离线的动静下加载页面会之后出现:

澳门新萄京官方网站 14

究其原因正是因为你在离线状态下不可能加载页面,以往有了 service worker,即便你在没网络的情况下,也能够加载此前缓存好的页面了。

第5步:创制可用的离线页面

离线页面能够是静态的HTML,一般用来提示用户日前恳请的页面一时半刻不只怕运用。可是,大家能够提供一些方可翻阅的页面链接。

Cache API可以在main.js中运用。不过,该API使用Promise,在不辅助Promise的浏览器中会失利,全体的JavaScript施行会为此面临震慑。为了防止那种景色,在拜访/js/offlinepage.js的时候大家增添了一段代码来检查当前是还是不是在离线情形中:

/js/offlinepage.js 中以版本号为名称保存了目前的缓存,获取具有U福睿斯L,删除不是页面包车型大巴U大切诺基L,将这么些UTiguanL排序然后将有着缓存的U宝马X5L呈以后页面上:

// 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,假使跨域的能源支撑CO凯雷德S,那么能够把request的mod改成cors。假诺请求战败了,如40四也许是过期等等的,那么也直接再次来到response让主页面管理,不然的话表达加载成功,把那几个response克隆二个内置cache里面,然后再回去response给主页面线程。注意能放缓存里的财富一般只好是GET,通过POST获取的是不能够缓存的,所以要做个推断(当然你也能够手动把request对象的method改成get),还有把一部分私人住房不期望缓存的财富也做个决断。

这么只要用户张开过贰遍页面,ServiceWorker就安装好了,他刷新页面或然张开第二个页面包车型地铁时候就能够把请求的财富11做缓存,包涵图片、CSS、JS等,只要缓存里有了不管用户在线只怕离线都能够不荒谬访问。那样大家自然会有二个题目,这一个缓存空间到底有多大?上1篇我们提到Manifest也总算地方存款和储蓄,PC端的Chrome是5Mb,其实那几个说法在新本子的Chrome已经不可信赖了,在Chrome 陆1本子能够见见本地存款和储蓄的空仲春动用意况:

澳门新萄京官方网站 15

当中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-伍.js,可是PWA又供给大家能缓存页面html。那如何做吧?谷歌(谷歌)的开采者文书档案它只是提到会存在那一个主题素材,但并未有证实怎么化解那么些标题。那个的标题标缓和将须求大家要有三个编写制定能知道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 U路虎极光L(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,假诺是的话就去发个请求获取2个文件,依照这些文件的剧情决定是不是要求删除缓存,这几个立异的函数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});
        });
    }
};

代码先去获得1个json文件,二个页面会对应2个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: []}}

中间根本有2个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: 一就代表那是八个翻新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也不会缓存了。

还有1种更新是用户更新的,比方用户公布了评价,须要在页面文告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应用是1种新的技艺,所以利用的时候势须求小心。也正是说,渐进式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可能会回来30四 Not Modified,不然正常重回200和数据内容。那正是整八个赢得的进程。

故此倘若既用了Manifest又用ServiceWorker的话应该会招致同三个能源存了两遍。不过能够让帮助ServiceWorker的浏览器接纳瑟维斯 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 首回加载的时候缓存暗中同意页面,留下退路。

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

那样就保证了用户率先次加载页面,马上离线访问站点后,即便不可能像第3回同样能够服务端渲染数据,可是随后能经过猎取页面,异步加载数据的方法创设离线应用。

澳门新萄京官方网站 16

用户率先次访问站点,借使在不刷新页面包车型客车气象切换路由到任何页面,则会异步获取到的多寡,当下次做客对应的路由的时候,则失利到异步获取数据。

澳门新萄京官方网站 17

当用户第3次加载页面包车型客车时候,因为 service worker 已经决定了站点,已经有所了缓存页面包车型客车力量,之后在造访的页面都将会被缓存大概更新缓存,当用户离线访问的的时候,也能访问到服务端渲染的页面了。

澳门新萄京官方网站 18

URL隐藏

当您的利用就是四个单U中华VL的应用程序时(举个例子游戏),小编建议您隐藏地址栏。除此而外的图景本人并不提出你隐藏地址栏。在Manifest中,display: minimal-ui 或者 display: browser对此绝大好多情形来讲丰盛用了。

五. 施用Web App Manifest增添桌面入口

只顾这里说的是别的贰个Manifest,那么些Manifest是3个json文件,用来放网址icon名称等音讯以便在桌面增添二个Logo,以及创立一种张开这些网页如同张开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": "48x48" }, { "src": "/html/app-manifest/logo_96.png", "type": "image/png", "sizes": "96x96" }, { "src": "/html/app-manifest/logo_192.png", "type": "image/png", "sizes": "192x192" }, { "src": "/html/app-manifest/logo_512.png", "type": "image/png", "sizes": "512x512" } ], "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": "48x48"
    },
    {
      "src": "/html/app-manifest/logo_96.png",
      "type": "image/png",
      "sizes": "96x96"
    },
    {
      "src": "/html/app-manifest/logo_192.png",
      "type": "image/png",
      "sizes": "192x192"
    },
    {
      "src": "/html/app-manifest/logo_512.png",
      "type": "image/png",
      "sizes": "512x512"
    }
  ],
  "start_url": "/?launcher=true",
  "display": "standalone",
  "background_color": "#287fc5",
  "theme_color": "#fff"
}

icon须要安不忘忧种种尺度,最大必要51二px * 51二px的,那样Chrome会自动去采纳合适的图形。假如把display改成standalone,从转换的Logo张开就能够像张开一个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 Worker缓存:
澳门新萄京官方网站 19把start_url指向的页面用ServiceWorker缓存起来,那样当用户用Chrome浏览器展开那一个网页的时候,Chrome就可以在尾巴部分弹1个唤起,询问用户是还是不是把那些网页增添到桌面,如若点“增多”就能够转移3个桌面Logo,从那几个Logo点进去就好像张开叁个App同样。感受如下:

澳门新萄京官方网站 20

比较难堪的是Manifest近日只有Chrome援助,并且不得不在安卓系统上应用,IOS的浏览器无法增添1个桌面Logo,因为IOS未有开放那种API,可是本人的Safari却又是足以的。

综上,本文介绍了怎么用Service Worker结合Manifest做一个PWA离线Web 应用程式,重如若用ServiceWorker调节缓存,由于是写JS,比较灵敏,还是能够与页面进行通讯,其余通过请求页面包车型大巴翻新时间来判断是或不是要求更新html缓存。ServiceWorker的包容性不是特地好,可是前景比较光明,浏览器都在备选扶助。现阶段能够构成offline cache的Manifest做离线应用。

相关阅读:

  1. 为啥要把网站晋级到HTTPS
  2. 怎么样把网址晋级到http/二
  3. 本人是怎么让网址用上HTML5Manifest

1 赞 1 收藏 评论

澳门新萄京官方网站 21

接口缓存战术

谈完页面缓存,再来说讲接口缓存,接口缓存就跟页面缓存很类似了,唯1的分化在于:页面第二回加载的时候不必然有缓存,可是会有接口缓存的存在(因为伪造了 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 离线缓存应用的效用了,不过还有依然存在一些难题:

缓存刷新

以身作则代码中在提倡呼吁此前会先查询缓存。当用户处于离线状态时,这很好,但是如果用户处于在线状态,那她只会浏览到相比较老旧的页面。

各个资源举例图片和录制不会变动,所以一般都把这么些静态能源设置为长时间缓存。那么些财富得以直接缓存一年(3一,53陆,000秒)。在HTTP Header中,正是:

Cache-Control: max-age=31536000

1
Cache-Control: max-age=31536000

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

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 收藏 评论

澳门新萄京官方网站 22

绝不强缓存 sw.js

用户每回访问页面包车型地铁时候都会去重新赢得 sw.js,依照文件内容跟此前的本子是不是同样来剖断 service worker 是或不是有创新。所以一旦你对 sw.js 开启强缓存的话,就将陷入死循环,因为老是页面获得到的 sw.js 都以1律,那样就不恐怕晋升你的 service worker。

除此以外对 sw.js 开启强缓存也是从未有过需求的:

  1. 本人 sw.js 文件本身就十分小,浪费不了多少带宽,感觉浪费能够应用协议缓存,但附加增支担负
  2. 澳门新萄京官方网站,sw.js 是在页面空闲的时候才去加载的,并不会影响用户首屏渲染速度

幸免改动 sw 的 UBMWX伍L

在 sw 中如此做是“最差施行”,要在原地方上修修改改 sw。

比释尊验证为啥:

  1. index.html 注册了 sw-v1.js 作为 sw
  2. sw-v壹.js 对 index.html 做了缓存,也正是缓存优先(offline-first)
  3. 您更新了 index.html 重新挂号了在新鸿基土地资金财产点的 sw sw-v贰.js

1经你像下边那么做,用户恒久也拿不到 sw-v二.js,因为 index.html 在 sw-v壹.js 缓存中,那样的话,假若你想翻新为 sw-v2.js,还亟需转移原来的 sw-v壹.js。

测试

尔后,我们早已达成了动用 service worker 对页面实行离线缓存的效率,假使想体验效果的话,访问作者的博客:

随意浏览任性的页面,然后关掉互联网,再度走访,此前您浏览过的页面都足以在离线的景观下开始展览访问了。

IOS 供给 1一.三 的本子才支撑,使用 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: 设置Logo,插件会活动帮你转移不一致 size 的图样,可是图片大小必须高出最大 sizes
  6. ios: 设置在 safari 中如何去增添桌面应用

设置完事后,webpack 会在塑造进度中变化对应的 manifest 文件,并在 html 文件中援引,下边正是生成 manifest 文件:

{ "icons": [ { "src": "/icons/icon_512x512.79ddc5874efb8b481d9a3d06133b6213.png", "sizes": "512x512", "type": "image/png" }, { "src": "/icons/icon_384x384.09826bd1a5d143e05062571f0e0e86e7.png", "sizes": "384x384", "type": "image/png" }, { "src": "/icons/icon_256x256.d641a3644ce20c06855db39cfb2f7b40.png", "sizes": "256x256", "type": "image/png" }, { "src": "/icons/icon_192x192.8f11e077242cccd9c42c0cbbecd5149c.png", "sizes": "192x192", "type": "image/png" }, { "src": "/icons/icon_128x128.cc0714ab18fa6ee6de42ef3d5ca8fd09.png", "sizes": "128x128", "type": "image/png" }, { "src": "/icons/icon_96x96.dbfccb1a5cef8093a77c079f761b2d63.png", "sizes": "96x96", "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": "512x512",
      "type": "image/png"
    },
    {
      "src": "/icons/icon_384x384.09826bd1a5d143e05062571f0e0e86e7.png",
      "sizes": "384x384",
      "type": "image/png"
    },
    {
      "src": "/icons/icon_256x256.d641a3644ce20c06855db39cfb2f7b40.png",
      "sizes": "256x256",
      "type": "image/png"
    },
    {
      "src": "/icons/icon_192x192.8f11e077242cccd9c42c0cbbecd5149c.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "/icons/icon_128x128.cc0714ab18fa6ee6de42ef3d5ca8fd09.png",
      "sizes": "128x128",
      "type": "image/png"
    },
    {
      "src": "/icons/icon_96x96.dbfccb1a5cef8093a77c079f761b2d63.png",
      "sizes": "96x96",
      "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 文件是或不是见效:

澳门新萄京官方网站 23

那般表明你的安插生效了,安卓机会自动识别你的布局文件,并问询用户是不是丰裕。

结尾

讲到那基本上就完了,等之后 IOS 帮助 PWA 的其余效率的时候,到时候小编也会相应地去奉行别的 PWA 的特点的。以后 IOS 11.三 也仅仅支持 PWA 中的 service worker 和 app manifest 的成效,不过相信在不久的昨天,其余的功效也会相应得到援助,到时候相信 PWA 将会在运动端绽放异彩的。

1 赞 收藏 评论

澳门新萄京官方网站 24

本文由澳门新萄京官方网站发布于澳门新萄京赌场网址,转载请注明出处:澳门新萄京官方网站:同构应用,离线网页应用

关键词: