H5 PWA技术

1、原生app优缺点

a、体验好、下载到手机上入口方便
  b、开发成本高(ios和安卓)
  c、软件上线需要审核
  d、版本更新需要将新版本上传到不同的应用商店
  e、使用前需下载

2、web网页优缺点

a、开发成本低、网站更新时上传最新的资源到服务器即可、手机自带浏览器打开即可
  b、体验比原生app差
  c、入口不便捷
  d、无网无相应,不具备离线能力
  e、无app的消息推送

3、PWA是什么?

PWA是一个新的前端技术,全称:Progressive Web App,这是一个渐进式的网页应用程序。结合了一系列现代web技术,在网页应用中实现和原生应用相近的用户体验。

PWA的三个关键词:

Reliable(可靠的):当用户从手机屏幕启动时,无需考虑网络状态,可以立刻加载出PWA
  Fast(快速的):加载速度快
  Engaging(可参与的):PWA可以添加在用户的主屏幕上,无需从应用商店里下载,他们通过网络应用程序Manifest file提供类似于APP的使用体验(android上可设置全屏显示,由于Safari支持度的问题ios不可以),可以进行“推送通知”

小小总结:

a、解决的问题:
      1>可添加至主屏幕
      2>实现离线缓存功能
      3>实现消息推送

b、优势:几乎瞬间加载,但安全且富有弹性
  c、核心:manifest文件清单、Service Workers

4、Manifest

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


  Minimal PWA
  
  
  
  

manifest.json

{
  "name": "Minimal PWA", // 必填 显示的插件名称
  "short_name": "PWA Demo", // 可选  在APP launcher和新的tab页显示,如果没有设置,则使用name
  "description": "The app that helps you understand PWA", //用于描述应用
  "display": "standalone", // 定义开发人员对Web应用程序的首选显示模式。standalone模式会有单独的
  "start_url": "/", // 应用启动时的url
  "theme_color": "#313131", // 桌面图标的背景色
  "background_color": "#313131", // 为web应用程序预定义的背景颜色。在启动web应用程序和加载应用程序的内容之间创建了一个平滑的过渡。
  "icons": [ // 桌面图标,是一个数组
    {
    "src": "icon/lowres.webp",
    "sizes": "48x48",  // 以空格分隔的图片尺寸
    "type": "image/webp"  // 帮助userAgent快速排除不支持的类型
  },
  {
    "src": "icon/lowres",
    "sizes": "48x48"
  },
  {
    "src": "icon/hd_hi.ico",
    "sizes": "72x72 96x96 128x128 256x256"
  },
  {
    "src": "icon/hd_hi.svg",
    "sizes": "72x72"
  }
  ]
}

5、Service Worker**

SW 是什么呢?这个是离线缓存文件。我们 PWA 技术使用的就是它!SW 是浏览器在后台独立于网页运行的脚本,它打开了通向不需要网页或用户交互的功能的大门,因为使用了它,才会有的那个 Reliable 特性吧,SW 作用于 浏览器于服务器之间,相当于一个代理服务器。

  • 基本特点

  • 运行在它自己的全局脚本上下文中

  • 不绑定到具体的网页

  • 无法修改网页中的元素,因为它无法访问 DOM

  • 只能使用 HTTPS

  • 拦截进出的 HTTP 请求,从而完全控制你的网站

  • 与主JS线程独立,不会被阻塞

  • 完全异步,无法使用localStorage

功能(还是比较逆天的)

  • 后台数据的同步
  • 从其他域获取资源请求
  • 接受计算密集型数据的更新,多页面共享该数据
  • 客户端编译与依赖管理
  • 后端服务的hook机制
  • 根据URL模式,自定义模板
  • 性能优化
  • 消息推送
  • 定时默认更新
  • 地理围栏

生命周期:

image
  • 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 中查看

如果上个图不好理解,可以看这个,把它的生命周期看成红绿灯:

image
  • register (需要下载和解析,红灯)
  • install (执行,黄灯)
  • activated( 成功,绿灯)

当用户首次导航至 URL 时,服务器会返回响应的网页

  • 第1步:当你调用 register() 函数时, Service Worker 开始下载。
  • 第2步:在注册过程中,浏览器会下载、解析并执行 Service Worker ()。如果在此步骤中出现任何错误,register() 返回的 promise 都会执行 reject 操作,并且 Service Worker 会被废弃。
  • 第3步:一旦 Service Worker 成功执行了,install 事件就会激活
  • 第4步:安装完成,Service Worker 便会激活,并控制在其范围内的一切。如果生命周期中的所有事件都成功了,Service Worker 便已准备就绪,随时可以使用了!

6、实现离线缓存



  
    
    Hello Caching World!
  
  
    
                     
    
         
    
  

注意:

注:Service Worker 的注册路径决定了其 scope 默认作用页面的范围。
如果 service-worker.js 是在 /sw/ 页面路径下,这使得该 Service Worker 默认只会收到 页面/sw/ 路径下的 fetch 事件。
如果存放在网站的根路径下,则将会收到该网站的所有 fetch 事件。
如果希望改变它的作用域,可在第二个参数设置 scope 范围。示例中将其改为了根目录,即对整个站点生效。
service-worker.js

var cacheName = 'helloWorld';     // 缓存的名称  
// install 事件,它发生在浏览器安装并注册 Service Worker 时        
self.addEventListener('install', event => { 
/* event.waitUtil 用于在安装成功之前执行一些预装逻辑
 但是建议只做一些轻量级和非常重要资源的缓存,减少安装失败的概率
 安装成功后 ServiceWorker 状态会从 installing 变为 installed */
  event.waitUntil(
    caches.open(cacheName)                  
    .then(cache => cache.addAll([    // 如果所有的文件都成功缓存了,便会安装完成。如果任何文件下载失败了,那么安装过程也会随之失败。        
      '/js/script.js',
      '/images/hello.png'
    ]))
  );
});
  
/**
为 fetch 事件添加一个事件监听器。接下来,使用 caches.match() 函数来检查传入的请求 URL 是否匹配当前缓存中存在的任何内容。如果存在的话,返回缓存的资源。
如果资源并不存在于缓存当中,通过网络来获取资源,并将获取到的资源添加到缓存中。
*/

self.addEventListener('fetch', function (event) {
  event.respondWith(
    caches.match(event.request)                  
    .then(function (response) {
      if (response) {                            
        return response;                         
      }
      var requestToCache = event.request.clone();  //          
      return fetch(requestToCache).then(                   
        function (response) {
          if (!response || response.status !== 200) {      
            return response;
          }
          var responseToCache = response.clone();          
          caches.open(cacheName)                           
            .then(function (cache) {
              cache.put(requestToCache, responseToCache);  
            });
          return response;             
    })
  );
});

注意:

注:为什么用request.clone()和response.clone()
需要这么做是因为request和response是一个流,它只能消耗一次。因为我们已经通过缓存消耗了一次,然后发起 HTTP 请求还要再消耗一次,所以我们需要在此时克隆请求
Clone the request—a request is a stream and can only be consumed once.

7、消息推送

步骤一、提示用户并获得他们的订阅详细信息
步骤二、将这些详细信息保存在服务器上
步骤三、在需要时发送任何消息
前两步:

index.html



  
    
    Progressive Times
                                          
  
  
    
  

步骤三:

app.js

const webpush = require('web-push');                 
const express = require('express');
var bodyParser = require('body-parser');
const app = express();
webpush.setVapidDetails(                             
  'mailto:[email protected]',
  'BAyb_WgaR0L0pODaR7wWkxJi__tWbM1MPBymyRDFEGjtDCWeRYS9EF7yGoCHLdHJi6hikYdg4MuYaK0XoD0qnoY',
  'p6YVD7t8HkABoez1CvVJ5bl7BnEdKUu5bSyVjyxMBh0'
);
app.post('/register', function (req, res) {           
  var endpoint = req.body.endpoint;
  saveRegistrationDetails(endpoint, key, authSecret); 
  const pushSubscription = {                          
    endpoint: req.body.endpoint,
    keys: {
      auth: req.body.authSecret,
      p256dh: req.body.key
    }
  };
  var body = 'Thank you for registering';
  var iconUrl = 'https://example.com/images/homescreen.png';
  // 发送 Web 推送消息
  webpush.sendNotification(pushSubscription,          
      JSON.stringify({
        msg: body,
        url: 'http://localhost:3111/',
        icon: iconUrl
      }))
    .then(result => res.sendStatus(201))
    .catch(err => {
      console.log(err);
    });
});
app.listen(3111, function () {
  console.log('Web push app listening on port 3111!')
});

service worker监听push事件,将通知详情推送给用户

service-worker.js

self.addEventListener('push', function (event) {
 // 检查服务端是否发来了任何有效载荷数据
  var payload = event.data ? JSON.parse(event.data.text()) : 'no payload';
  var title = 'Progressive Times';
  event.waitUntil(
    // 使用提供的信息来显示 Web 推送通知
    self.registration.showNotification(title, {                           
      body: payload.msg,
      url: payload.url,
      icon: payload.icon
    })
  );
});

8、PWA小demo

准备:

创建一个关于PWA项目的文件夹

文件夹内准备一张图

一个index.html文件

一个main.css文件

一个manifest.json文件

一个sw.js文件


image

css文件夹里有一个style.css

images里有一个logo.jpg

具体看代码:

index.html




    
    
    
    Hello PWA
    



    

    


我的style.css是空的哈哈哈

sw.js

var cacheName = 'hello-pwa';

self.addEventListener('install', event => {
    event.waitUntil(
        caches.open(cacheName)
          .then(cache => cache.addAll(
            [
                '/',  // 这个一定要包含整个目录,不然无法离线浏览
                './images/logo.jpg',
                './index.html',
                './css/style.css'
            ]
          )).then(() => self.skipWaiting())
    );
});

self.addEventListener('fetch', function (event) {  
    event.respondWith(
      caches.match(event.request)                    
      .then(function (response) {
        if (response) {                              
          return response;                           
        }
        return fetch(event.request);                 
      })
    );
});

接下来通过 http-server 和 ngrok(https)进行调试查看

在当前文件下
安装 http-server

npm install http-server -g

安装 ngrok,下载解压即可

在项目目录下执行如下命令:

http-server -c-1  // -c-1 会关闭缓存

再开启另外一个终端在 ngrok 文件的目录下执行如下命令:

./ngrok http 8080 // http-server 默认开启8080端口

运行:(我端口8080被占了,所以这里是8081)
image

查看application

image

查看缓存部分

image

第一次加载进来缓存没有东西,需要刷新一下页面。

https://github.com/yangTwo100/PWA_search_demo

以上。

你可能感兴趣的:(H5 PWA技术)