PWA配置教程

PWA

一、PWA 简介

一个新的前端技术,PWA( 全称:Progressive Web App )也就是说这是个渐进式的网页应用程序。
是 Google 在 2015 年提出,2016年6月才推广的项目。是结合了一系列现代Web技术的组合,在网页应用中实现和原生应用相近的用户体验。

PWA官网:https://developers.google.com/web/progressive-web-apps/

1.1 PWA关键技术

  • Service Worker (可以理解为服务工厂)
  • Manifest (应用清单)
  • Push Notification(推送通知)
  • Service Worker

1.2 浏览器支持:

顺便带一句:目前只能在 HTTPS 环境下才能使用SW,因为SW 的权利比较大,能够直接截取和返回用户的请求,所以要考虑一下安全性问题。
事件机制
功能(还是比较逆天的)
后台数据的同步
从其他域获取资源请求
接受计算密集型数据的更新,多页面共享该数据
客户端编译与依赖管理
后端服务的hook机制
根据URL模式,自定义模板
性能优化
消息推送
定时默认更新
地理围栏

1.3 生命周期:

  • Parsed (解析成功): 首次注册 SW 时,浏览器解决脚本并获得入口点,如果解析成功,就可以访问到 SW 注册对象,在这一点中我们需要在 HTML 页面中添加一个判断,判断该浏览器是否支持 SW 。
  • Installing (正在安装):SW 脚本解析完成之后,浏览器会尝试进行安装,installing 中 install 事件被执行,如果其中有 event.waitUntil ( ) 方法,则 installing 事件会一直等到该方法中的 Promise 完成之后才会成功,如果 Promise 被拒绝,则安装失败,SW会进入 Redundant( 废弃 )状态。
  • Installed / Waiting (安装成功/等待中):如果安装成功,SW 将会进入这个状态。
  • Activating (正在激活):处于 waiting 状态的 SW 发生以下情况,将会进入 activating 状态中:当前已无激活状态的 worker 、 SW脚本中的 self.skipWaiting()方法被调用 ( ps: self 是 SW 中作用于全局的对象,这个方法根据英文翻译过来也能明白什么意思啦,跳过等待状态 )、用户已关闭 SW 作用域下的所有页面,从而释放了当前处于激活状态的 worker、超出指定时间,从而释放当前处于激活状态的 worker
  • Activated (激活成功):该状态,其成功接收了 document 全面控制的激活态 worker 。
  • Redundant (废弃):这个状态的出现时有原因的,如果 installing 事件失败或者 activating 事件失败或者新的 SW 替换其成为激活态 worker 。installing 事件失败和 activating 事件失败的信息我们可以在 Chrome 浏览器的 DevTools 中查看
    一个很不错的全面介绍sw的教程:https://www.villainhr.com/page/2017/01/08/Service%20Worker%20%E5%85%A8%E9%9D%A2%E8%BF%9B%E9%98%B6

1.4 Manifest

Web App Manifest 是一个 W3C 规范,它定义了一个基于 JSON 的 List 。Manifest 在 PWA 中的作用有:

  • 能够将你浏览的网页添加到你的手机屏幕上
  • 在 Android 上能够全屏启动,不显示地址栏 ( 由于 Iphone 手机的浏览器是 Safari ,所以不支持哦)
  • 控制屏幕 横屏 / 竖屏 展示
  • 定义启动画面
  • 可以设置你的应用启动是从主屏幕启动还是从 URL 启动
  • 可以设置你添加屏幕上的应用程序图标、名字、图标大小

1.5 Push Notification

Push 和 Notification 是两个不同的功能,涉及到两个 API 。

  • Notification 是浏览器发出的通知消息。
  • Push 和 Notification 的关系,Push:服务器端将更新的信息传递给 SW ,Notification: SW 将更新的信息推送给用户。

二、PWA 注册

2.1 添加注册事件

在网页中添加如下内容:

<script>
if ('serviceWorker' in navigator) {
    window.addEventListener('load', function () {
        navigator.serviceWorker.register('/sw.js', {scope: '/'})
            .then(function (registration) {
                // 注册成功
                console.log('ServiceWorker registration successful');
            })
            .catch(function (err) {
                // 注册失败:(
                console.log('ServiceWorker registration failed');
            });
    });
}
</script>

2.2 添加 PWA 应用清单信息

在网页头部加入:

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

然后在同一目录下新建 manifest.json 文件如下:

{
    "name": "应用名称",
    "short_name": "应用名称",
    "display": "standalone",
    "icons": [
        {  "src": "/512.png", "type": "image/png", "sizes": "512x512"  },
        {  "src": "/256.png", "type": "image/png", "sizes": "256x256"  },
        {  "src": "/128.png", "type": "image/png", "sizes": "128x128"  },
        {  "src": "/_64.png", "type": "image/png", "sizes": "64x64"  }
    ],
    "start_url": "/",
    "background_color":"#F9F9F9",
    "theme_color": "#3A3F51"
}

注:以上信息根据实际情况修改。

三、Service Worker 配置

3.1 Service Worker 简单配置参考模板

在网页同目录新建 sw.js 文件如下:

//缓存空间名称
var CACHE_VERSION = 'sw_v1';
//需缓存的文件
var CACHE_FILES = [
  '/',
  '/aaa.html',
  '/bbb.jpg'
];

//监听安装事件
self.addEventListener('install', function (event) {
  event.waitUntil(
    caches.open(CACHE_VERSION)
    .then(cache => cache.addAll(CACHE_FILES)
      .then(() => self.skipWaiting())
    ));
});

//监听激活事件
self.addEventListener('activate', function (event) {
  event.waitUntil(
    caches.keys().then(function (keys) {
      return Promise.all(keys.map(function (key, i) {
        if (key !== CACHE_VERSION) {
          return caches.delete(keys[i]);
        }
      }));
    })
  );
});

//不缓存策略
self.addEventListener('fetch', function(event) {});

注:以上为只缓存已定义的文件,不缓存其他。

3.2 Service Worker 更多缓存策略

3.2.1 不缓存策略

注:其余配置与上文简单配置内容一致,此策略与上文一致。

//不缓存策略
self.addEventListener('fetch', function(event) {});

3.2.2 总是更新策略

注:其余配置与上文简单配置内容一致,仅需将 fetch 事件修改如下。

//总是更新策略
self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.open(CACHE_VERSION).then(function(cache) {
      return fetch(event.request).then(function(response) {
        cache.put(event.request, response.clone());
        return response;
      });
    })
  );
});

3.2.3 先缓存后更新策略

注:其余配置与上文简单配置内容一致,仅需将 fetch 事件修改如下。

//先缓存后更新策略
self.addEventListener('fetch', function (event) {
  event.respondWith(
    caches.open(CACHE_VERSION).then(function (cache) {
      return cache.match(event.request).then(function (response) {
        var fetchPromise = fetch(event.request).then(function (networkResponse) {
          cache.put(event.request, networkResponse.clone());
          return networkResponse;
        })
        return response || fetchPromise;
      })
    })
  );
});

3.2.4 最佳策略

这里,我们简单的通过文件类型这个点来进行划分,优先情况是缓存 js/css 文件。这样通过在 FILE_LISTS 添加想要缓存的文件类型即可。之后,我们只要在 message 中更新原来的 document 即可。
注:此策略与上文简单配置配置内容不一致,以下为该策略配置示例:

//最佳策略
var CURRENT_CACHES = {
  prefetch: 'prefetch-cache-v' + 1,
};
var FILE_LISTS = ['js','css','png'];

var goSaving = function(url){
  for(var file of FILE_LISTS){
    if(url.endsWith(file)) return true;
  }
  return false;
}

self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request).then(function(resp) {
      return resp || fetch(event.request).then(function(response) {
          // 检查是否需要缓存
          var url = event.request.url;
          if(!goSaving(url))return response;
          console.log('save file:' + url);
          // 需要缓存,则将资源放到 caches Object 中
          return caches.open(CURRENT_CACHES.prefetch).then(function(cache) {
            console.log('update files like' + event.request.url);
            cache.put(event.request, response.clone());
            return response;
          });
        });
    })
  );
});
self.addEventListener('message',event =>{
  console.log("receive message" + event.data);
  // 更新根目录下的 html 文件。
  var url = event.data;
  console.log("update root file " + url);
  event.waitUntil(
    caches.open(CURRENT_CACHES.prefetch).then(cache=>{
        return fetch(url)
          .then(res=>{
            cache.put(url,res);
          })
    })
  )
});

更多文档请参阅官方网站。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,366评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,521评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,689评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,925评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,942评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,727评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,447评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,349评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,820评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,990评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,127评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,812评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,471评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,017评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,142评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,388评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,066评论 2 355