PWA 不是单纯的某项技术,必威:本质上是 Web A

结尾

讲到这几乎就完了,等将来 IOS 援救 PWA 的其余功能的时候,到时候作者也会相应地去推行别的 PWA 的性子的。以后 IOS 11.3 也唯有帮忙 PWA 中的 service worker 和 app manifest 的功用,可是相信在不久的前些天,其余的功用也会相应获得援助,到时候相信 PWA 将会在运动端盛开异彩的。

1 赞 收藏 评论

必威 1

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

2018/05/25 · 基本功技艺 · PWA

原来的文章出处: Craig Buckler   译文出处:山葫芦城控件   

上篇作品我们对渐进式Web应用(PWA)做了一些基本的介绍。

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

在这一节中,我们将介绍PWA的规律是何许,它是何许起头工作的。

2. Service Worker的补助景况

Service Worker前段时间唯有Chrome/Firfox/Opera帮助:

必威 2

Safari和Edge也在预备帮忙Service Worker,由于ServiceWorker是Google着力的一项专门的学业,对于生态相比密闭的Safari来讲也是迫于时局起头希图帮忙了,在Safari TP版本,能够看来:

必威 3

在实验成效(Experimental Features)里曾经有ServiceWorker的菜单项了,只是就算张开也是不可能用,会提示您还平昔不兑现:

必威 4

但无论是怎么样,至少表明Safari已经希图扶助ServiceWorker了。其他还是能见到在当年二零一七年五月发表的Safari 11.0.1本子现已补助WebRTC了,所以Safari还是一个进步的男女。

艾德ge也策动扶助,所以Service Worker的前景特别美好。

Service Worker生命周期

serviceworker的选择流程能够简单计算为注册--安装--激活。

注册其实就是告诉浏览器serviceworkerJS文件存放在怎么着岗位,然后浏览器下载、分析、实践该文件,进而运维安装。这里笔者创设一个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;

}

}));

})

);

});

离线缓存

  上面来经过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'
        },
      ]
    })
  ]

 

接口缓存计谋

谈完页面缓存,再来讲讲接口缓存,接口缓存就跟页面缓存很类似了,独一的例外在于:页面第叁回加载的时候不必然有缓存,然而会有接口缓存的存在(因为伪造了 cache 中的数据),所以缓存战术跟页面缓存类似:

  1. 互联网优先的方法,即优先得到网络上接口数据。当互连网诉求战败的时候,再去得到service worker 里之前缓存的接口数据
  2. 当网络加载成功现在,就更新 cache 中对应的缓存接口数据,保险下一次历次加载页面,都以上次访谈的新式接口数据

所以代码就疑似这么(代码类似,不再赘言):

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

此地其实能够再开展优化的,比方在获取数据接口的时候,能够先读取缓存中的接口数据进行渲染,当真正的网络接口数据再次回到之后再拓宽替换,这样也能管用压缩客商的首屏渲染时间。当然那说不定会发出页面闪烁的功能,可以增加一些动画片来拓宽交接。

Install事件

该事件将在利用设置到位后触发。大家一般在此处运用Cache API缓存一些少不了的文本。

首先,大家须求提供如下配置

  1. 缓存名称(CACHE)以及版本(version)。应用能够有三个缓存存储,然而在采用时只会使用个中二个缓存存储。每当缓存存储有变化时,新的本子号将会钦赐到缓存存款和储蓄中。新的缓存存款和储蓄将会作为当前的缓存存款和储蓄,以前的缓存存款和储蓄将会被作废。
  2. 一个离线的页面地址(offlineU陆风X8L):当客商访谈了前头从没访谈过的地方时,该页面将会显得。
  3. 三个含有了具备必需文件的数组,满含保持页面平常功用的CSS和JavaScript。在本示例中,笔者还增多了主页和logo。当有例外的U奇骏L指向同贰个财富时,你也能够将那一个U昂科威L分别写到这几个数组中。offlineU奥迪Q5L将会加入到那么些数组中。
  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);
    });
}

最终,大家增加一个install的风波监听器。waitUntil办法保险了service worker不会设置直到其连带的代码被实施。这里它会实践installStaticFiles()方法,然后self.skipWaiting()艺术来激活service worker:

// 应用设置 self.addEventListener('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())
  );
});

3. 使用Service Worker

ServiceWorker的应用套路是先挂号三个Worker,然后后台就能运转一条线程,能够在那条线程运营的时候去加载一些资源缓存起来,然后监听fetch事件,在这么些事件里拦截页面包车型大巴诉求,先看下缓存里有未有,假若有直接重返,不然寻常加载。大概是一齐先不缓存,每种财富央浼后再拷贝一份缓存起来,然后下一回呼吁的时候缓存里就有了。

Service Worker

ServiceWorker是PWA的主题技巧,它可感到web应用提供离线缓存功用,当然不止如此,上边罗列了部分ServiceWorker的特色:

听他们说HTTPS 处境,这是塑造PWA的硬性前提。(LAMP情状网址晋级HTTPS建设方案)

是叁个单身的 worker 线程,独立于近期网页进程,有本人独自的 worker context

可掣肘HTTP央浼和响应,可缓存文件,缓存的文书能够在互联网离线状态时取到

能向客商端推送音讯

不可能从来操作 DOM

异步完结,内部大都以通过 Promise 完毕

累加到荧屏

  没人愿意神经过敏地在运动设备键盘上输入长长的网站。通过加多到显示屏的功效,顾客能够像从使用商号安装本机应用那样,选取为其配备加上一个急迅链接,并且经过要顺遂得多

【配置项表达】

  使用manifest.json文件来贯彻增进到显示屏的效果与利益,上边是该公文内的配置项

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

  下边是一份健康的manifest.json文件的计划

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

【注意事项】

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

  2、Logo的种类最佳是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')))

 

其余难题

到前些天告竣,已经大半能够完结 service worker 离线缓存应用的功能了,可是还大概有依旧存在一些难题:

开垦者工具

Chrome浏览器提供了一比比皆是的工具来援救你来调度ServiceWorker,日志也会直接展现在调控台上。

你最棒使用匿有名的模特式来进展付出专门的学问,那样能够去掉缓存对开拓的干扰。

最后,Chrome的Lighthouse扩展也足感觉您的渐进式Web应用提供一些考订新闻。

(4)cache html

地方第(3)步把图纸、js、css缓存起来了,不过要是把页面html也缓存了,比如把首页缓存了,就能够有三个不知该笑还是该哭的题目——ServiceWorker是在页面注册的,可是今后收获页面包车型客车时候是从缓存取的,每趟都是同等的,所以就导致不恐怕立异ServiceWorker,如形成sw-5.js,可是PWA又供给我们能缓存页面html。那怎么办吧?Google的开发者文书档案它只是提到会存在那一个标题,但并不曾表达怎么消除这一个难题。那么些的难点的化解就要求大家要有二个体制能领悟html更新了,进而把缓存里的html给替换掉。

Manifest更新缓存的编写制定是去看Manifest的文本内容有没有爆发变化,假使发生变化了,则会去立异缓存,ServiceWorker也是依照sw.js的文件内容有未有发生变化,大家得以借鉴那几个思考,要是诉求的是html并从缓存里抽取来后,再发个伏乞获取叁个文件看html更新时间是还是不是发生变化,如若爆发变化了则表明发生改换了,从而把缓存给删了。所以能够在服务端通过决定这么些文件进而去创新客商端的缓存。如下代码:

JavaScript

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 U中华VL(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文件,贰个页面会对应二个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: []}}

其间主要有几个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缓存删了重新得到,那是一个转头的新闻文告:

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。客商公布完研讨后会触发刷新页面,刷新的时候缓存已经被删了就能够再也去央浼了。

这么就一举成功了实时更新的难点。

serviceworker的缓存成效

设置时,serviceworker将大家内定的静态财富拓宽缓存,你只怕会问,固然是实时的动态内容如何是好,大家不容许预先将富有的公文财富提前线指挥部定好,让serviceworker缓存。那将在涉及serviceworker的阻止HTTP央浼响应的特色了。

serviceworker拦截http恳求,首先去反省央浼的能源在缓存中是或不是存在,借使存在,则一向从缓存中调用,并且就是是无互联网状态下,serviceworker也能调用缓存中的财富。要是缓存中未有恳求的财富,则经过网络去服务器上查究,何况在找到并响应时,serviceworker会将其存入缓存中,以备下次再央求时一直从缓存中调用。

必威 5serviceworker缓存流程

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;

}

);

})

);

});

在网址前台通过浏览器开辟者工具,大家能够看下从缓存中调用财富的功效:

必威 6serviceworker缓存调用必威 7缓存文件

自己那边操作演示用的是谷歌(Google)浏览器,下面是各大浏览器对于serviceworker的支撑景况:

必威 8serviceworker浏览器帮助意况

概述

  PWA 的最重要特点包含上边三点:

  1、可信赖 - 尽管在不平稳的网络意况下,也能弹指间加载并突显

  2、体验 - 急速响应,况且有平整的卡通响应客商的操作

  3、粘性 - 像道具上的原生应用,具备沉浸式的顾客体验,客户能够增添到桌面

  首要意义包罗站点可增加至主显示屏、全屏格局运营、扶助离线缓存、新闻推送等

【PRPL模式】

  “PRPL”(读作 “purple”)是 Google 的程序员提议的一种 web 应用架构格局,它目的在于利用当代 web 平台的新本事以小幅度优化移动 web 的属性与感受,对怎么着组织与铺排高品质的 PWA 系统提供了一种高档次的虚幻

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

  1、PUSH/PRELOAD,推送/预加载初始 UEnclaveL 路由所需的严重品质源

  2、RENDE福睿斯,渲染开头路由,尽快让动用可被交互

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

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

【Service workers】

  Service Workers 是谷歌(Google) chrome 团队提议并大力推广的一项 web 技巧。在 二零一四 年,它参加到 W3C 规范,步向草案阶段

  PWA 的关键在于 Service Workers 。就其大旨来讲,Service Workers 只是后台运维的 worker 脚本。它们是用 JavaScript 编写的,只需短短几行代码,它们便可使开采者能够阻止互连网央求,管理推送信息并实行非常多别样职分

  Service Worker 中用到的一部分全局变量:

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

  瑟维斯 Worker 的职业体制大约如下:客户访谈贰个享有 瑟维斯 Worker 的页面,浏览器就能下载那个 Service Worker 并尝试安装、激活。一旦激活,瑟维斯 Worker 就到后台起首专门的职业。接下来客户访谈这几个页面只怕每隔四个时段浏览器都会下载这一个Service Worker,假设监测到 Service Worker 有更新,就能重新安装并激活新的 Service Worker,同有时间 revoke 掉旧的 Service Worker,那便是 SW 的生命周期

  因为 Service Worker 有着近些日子的权柄接触数据,因此 Service Worker 只能被设置在 HTTPS 加密的页面中,纵然无形在那之中升高了 PWA 的门道,不过也是为着安全做思量

 

高速激活 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,原因不详,有读者明白的话能够沟通下。

当今当你首先次加载页面,跳转路由,登时离线访谈的页面,也能够高枕无忧地加载页面了。

URL隐藏

当您的使用便是贰个单URubiconL的应用程序时(举个例子游戏),笔者建议您遮掩地址栏。除外的情形自身并不建议你掩盖地址栏。在Manifest中,display: minimal-ui 或者 display: browser对此大多数场所包车型地铁话丰裕用了。

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

只顾这里说的是别的三个Manifest,那些Manifest是叁个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需求预备三种原则,最大须求512px * 512px的,那样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">

如此组合瑟维斯 Worker缓存:
必威 9把start_url指向的页面用ServiceWorker缓存起来,那样当顾客用Chrome浏览器打开这么些网页的时候,Chrome就能在底部弹八个提醒,询问客户是不是把那些网页增多到桌面,假设点“增添”就能够生成一个桌面Logo,从那么些Logo点进去就如张开三个App一样。感受如下:

必威 10

正如为难的是Manifest近年来唯有Chrome协理,并且只好在安卓系统上采用,IOS的浏览器不大概增多多个桌面Logo,因为IOS未有开放这种API,不过作者的Safari却又是能够的。

综上,本文介绍了怎么用瑟维斯 Worker结合Manifest做多个PWA离线Web 应用程式,首假如用ServiceWorker调控缓存,由于是写JS,相比灵敏,还足以与页面实行通信,别的通过央浼页面的更新时间来剖断是或不是须求创新html缓存。ServiceWorker的包容性不是非常好,可是前景比较光明,浏览器都在筹算援救。现阶段能够整合offline cache的Manifest做离线应用。

相关阅读:

  1. 怎么要把网址进级到HTTPS
  2. 怎么样把网址晋级到http/2
  3. 自己是怎么样让网址用上HTML5 Manifest

1 赞 1 收藏 评论

必威 11

加上到主屏

PWA匡助将web应用在主屏桌面上增多贰个快速方式,以有助于顾客快速访谈,同有时候升级web应用重复访问的概率。你可能会说,未来运动端上的浏览器功用列表里一般都提供了“增加到桌面”的功效呀,然则PWA与浏览器自带的丰盛到桌面包车型地铁完成形式不相同。

PWA无需客商特意去效能列表中动用那几个功用开关,而是在顾客访问web应用时,直接在界面中唤醒三个丰裕到主屏桌面的横幅,从web应用角度来讲,那事实上正是勇往直前与消沉的分别。

PWA完结该功用特别轻便,只必要三个manifest.json文件,文件中客商能够自定义应用的开发银行页面、模板颜色、图标等剧情。上面是自家的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系统的匡助并不佳,安卓手提式有线电话机上须选用57本子以上的Google浏览器能够支撑该作用,上边是自个儿在安卓手提式有线电话机上的操作演示:

必威 12足够到主屏

职能演示

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

【增加到桌面】

必威 13

【离线缓存】

   由于手提式有线电话机录屏选用不可能开展离线摄像,改由模拟器模拟离线效果

必威 14

 

本文由必威发布于必威-前端,转载请注明出处:PWA 不是单纯的某项技术,必威:本质上是 Web A

TAG标签:
Ctrl+D 将本页面保存为书签,全面了解最新资讯,方便快捷。