PWA渐进式web应用

PWA(Progressive Web App)是一种理念,使用多种技术来增强web app的功能,可以让网站的体验变得更好,能够模拟一些原生功能,比如通知推送。在移动端利用标准化框架,让网页应用呈现和原生应用相似的体验。

PWA核心技术

  • web app manifest
  • service worker
  • promise/async/await
  • fatch api
  • Cache storage
  • 常见缓存策略
  • notification

PWA四个核心内容

  1. manifest.json
  2. serviceWork
  3. cacheStrorage
  4. notification

1、manifest

  • pwa技术集合的一部分
  • 让网站安装到设备主屏幕上,不需要用户在商城下载
  • 在json文件中提供有关应用的信息
  • 启动页面避免生硬过度
  • 隐藏浏览器相关的UI 比如地址栏等

注意点

  1. manifest.json 配置
  2. index.html 中引入manifest.json
  3. 需要https或者http://localhost下访问

常见配置

  • name: 项目名称
  • short_name:短名称,用于主屏幕显示
  • start_url:设备应用加载的url 可以使绝对或者相对路径
  • icons : 应用图标144X144
  • background_color:启动动画背景色
  • theme_color:应用程序主题背景色
  • dispay:app显示模式 fullcreen全屏 standalone minimal-ui
{
    "name":"电影App",
    "short_name":"app",
    "start_url":"/index.html",
    "icon":[{
        "src":"icons/icon_fro.png",
        "sizes":"144x144",
        "type":"image/png"
    }],
    "background_color":"skyblue",
    "theme_color":"yellow",
    "display":"standalone"
}

2、service worker

  • 标准的PWA包括3个部分
    https或http://localhost
    manifest.json
    service work
  • 做离线缓存

web work使用

  • 创建web worker var worker = new Worker('work.js')
  • 在web work 中进行复杂计算
  • web work计算结束 通过self.postMessage(msg)给主线程发消息
  • 主线程通过 worker.onMessage = function(msg) 监听消息
  • 主线程可以用同样的方式给web worker进行通讯

service worker

  • web work 临时的,每次事情不能持久存下来。
  • servive work 类似于代理服务器,存在缓存里
  1. 一旦install ,永久存在,除非被手动unregister
  2. 用到的时候唤醒,不用自动休眠
  3. 必须https环境下工作
  4. 异步实现,内不通过Promise实现
  5. 可编程链接代理请求和返回,缓存文件,缓存的文件可以被网页进程取到(包括网络离线状态)

service worker使用步骤

  • window.onload 中注册service worker ,放在与其他资源竞争
  • navigator对象中内置了serviceWorker属性
  • service worker在老版本不支持,需要进行浏览器兼容 if('serviceWorker' in navigator){}
  • 注册service worker navigator.serviceWorker.register('./sw.js')返回一个promise对象

service worker生命周期事件

  • install: 注册成功触发,用户缓存资源
  • activate:激活时触发,删除旧的资源
  • fetch 发送请求时触发,操作缓存读取网络资源

fetch Api

config常见参数
body
headers
methods
index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <!-- 引入清单文件 -->
    <link rel="manifest" href="manifest.json">
</head>

<body>
    <h1>hello word</h1>
    <!-- web worker -->
    <script>
        const worker = new Worker('worker.js')
        worker.addEventListener('message', e => {
            console.log(e.data)
        })
    </script>
    <!-- service worker -->
    <script>
        // 1.需要网页加载完成注册
        window.onload = () => {
            // 2.能力监测
            if ('serviceWorker' in navigator) {
                navigator.serviceWorker.register('./sw.js').then(res => {
                    console.log(res)
                })
            }
        }
    </script>
</body>

</html>

webwork.js

// 独立的进程,不能做dom操作
let total = 0 
for(var i=0;i<1000000;i++){total+=i}
// 发消息给主线程,把结果给他
self.postMessage({total:total})

sw.js (serviceWorker)

console.log('service注册')
self.addEventListener('install', e => {
    console.log(e)
    /*  
     跳过等待,直接到activite状态 是一个promise
      self.skipWaiting() 
     */

    // 等待skipWaiting结束,activite状态 是一个promise
    e.waitUntil(self.skipWaiting())
})
self.addEventListener('activate', e => {
    //   表示service work激活后,利可获取控制权
    e.waitUntil(self.ClientRectList.claim())
})
self.addEventListener('fatch', e => {
    console.log(e)
})

3、cacheStorage基本使用

  • cache对象的存储,配合service work使用

api类似于数据库操作

  • caches.open(name).then(function(){}):用于打开缓存
  • caches.keys(数据库名):返回一个promise
  • caches.delete(数据库)

cache常用方法

cache接口缓存的request/response对象提供存储机制

  • cache.put(req,res)把请求当成key,把相应存储起来
  • cache.add(url) 根据url请求,吧响应结果存储起来
  • cache.addAll(urls)抓取url数组,把结果存起来
  • cache.match(req)获取req对应的response
    index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <!-- 引入清单文件 -->
    <link rel="manifest" href="manifest.json">
</head>
<body>
    <h1>hello word</h1>
    <script>
        window.addEventListener('load', async()=>{
            if('serviceWorker' in navigator){
                try {
                    const registration=await navigator.serviceWorker.register('./sw.js')
                    console.log('成功')
                } catch (error) {
                    console.log('失败')
                }
            }
        })
    </script>
</body>
</html>

sw.js

// 注册serviceWorker时间
const CACHENAME = 'chache_v1'
// 缓存内容
self.addEventListener('install', async (e) => {

    const cache = await caches.open(CACHENAME)
    await cache.addAll([
        '/',
        '/icons/icon_fro.png',
        'manifest.js'
    ])
    e.waitUntil(self.skipWaiting())
})
// 清除就得缓存
self.addEventListener('activate', async e => {
    const keys = await caches.keys()
    keys.forEach(key => {
        if (key == CACHENAME) {
            caches.delete(key)
        }
    })
    e.waitUntil(self.clients.claim())
})
// 请求时间触发
// 能请求:请求
// 不能请求:读取cachesStorage
self.addEventListener('fetch', e => { 
    // 请求对象
const req=e.request
// 给浏览器响应
e.respondWith(networkFirst(req))

})
// 网络有限
async function networkFirst(req){
    try {
        const fresh=await fetch(req)
        return fresh
    } catch (error) {
        // 去缓存读取
        const cache=await caches.open(CACHENAME)
        const cached=await cache.match(req)
        return cached
    }
}

4、notiication 通知

  • 配置桌面通知的
  • Notification.permisin 可以获得用户的授权情况
  1. default:未授权
  2. denied:拒绝
  3. granted:授权
  • Notification.requestPermission() 请求用户授权
  • new Notification(title,{body:''}) 给个通知

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <!-- 引入清单文件 -->
    <link rel="manifest" href="manifest.json">
</head>
<body>
    <h1>hello word</h1>
    <script>
        window.addEventListener('load', async()=>{
            if('serviceWorker' in navigator){
                try {
                    const registration=await navigator.serviceWorker.register('./sw.js')
                    console.log('成功')
                } catch (error) {
                    console.log('失败')
                }
            }
        })
        // 判断是否联网
        if(Notification.permission=='default'){
            // 请求提示权限
            Notification.requestPermission()
        }
        if(!navigator.onLine){
            new Notification('提示',{body:'当前没有网'})
        }
        window.addEventListener('online',e=>{
            new Notification('提示',{body:'又忘了,请刷新访问最新数据'})
   
        })
    </script>
</body>
</html>

静态资源缓存优先,动态数据请求有限

上一篇:Nginx工作原理


下一篇:Service worker实践 作用