最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 深入了解PWA工作原理

    正文概述 掘金(高山低谷)   2021-01-11   849

    前言:

    pwa 讲起来大家都有所耳闻, 在实际项目开发中会运用到,它最重要的一个作用就是可以实现客户端离线缓存,其次是服务端消息推送。我们在现有的一些webapp用户体验差(不能离线访问),用户粘性低(无法保存入口)而pwa就是为了解决这一系列问题(Progressive Web Apps),让webapp具有快速,可靠,安全等特点。

    PWA一系列用到的技术

    • Web App Manifest (生成桌面图标)
    • Service Worker  (离线访问)
    • Push Api & Notification Api (服务端消息推送)
    • App Shell & App Skeleton   (骨架屏)

    一.Web App Manifest

    将网站添加到桌面、更类似native的体验。

    针对安卓手机:

      <link rel="apple-touch-icon" href="/icon.png" />
    
    {
        "name": "PWA效果展示",  // 应用名称 
        "short_name": "PWA", // 桌面应用的名称  ✓ (兼容性)
        "display": "standalone", // fullScreen (standalone) minimal-ui browser ✓
        "start_url": "/", // 打开时的网址  ✓
        "icons": [{ // 设置桌面图片 icon图标
            "src": "/icon.png",
            "sizes": "144x144",
            "type": "image/png"
        }],
        "background_color": "#aaa", // 启动画面颜色
        "theme_color": "#aaa" // 状态栏的颜色
    }
    

    ios 不支持 manifest文件,可以通过 meta/link 私有属性进行设置

    <!-- 图标icon -->
    <link rel="apple-touch-icon" href="/icon.png"/>
    <!-- 添加到主屏后的标题 和 short_name一致 -->
    <meta name="apple-mobile-web-app-title" content="标题"> 
    <!-- 隐藏safari地址栏 standalone模式下默认隐藏 -->
    <meta name="apple-mobile-web-app-capable" content="yes" /> 
    <!-- 设置状态栏颜色 -->
    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" /> 
    

    二.Service Worker

    Service Worker特点:

    • 不能访问/操作dom
    • 会自动休眠,不会随浏览器关闭所失效(必须手动卸载)
    • 离线缓存内容开发者可控
    • 必须在https或者localhost下使用
    • 所有的api都基于promise

    生命周期:

    • 安装( installing ):这个状态发生在 Service Worker 注册之后,表示开始安装,触发 install 事件回调指定一些静态资源进行离线缓存。
    • 安装后( installed ):Service Worker 已经完成了安装,并且等待其他的 Service Worker 线程被关闭。
    • 激活( activating ):在这个状态下没有被其他的 Service Worker 控制的客户端,允许当前的 worker 完成安装,并且清除了其他的 worker 以及关联缓存的旧缓存资源,等待新的 Service Worker 线程被激活。
    • 激活后( activated ):在这个状态会处理 activate 事件回调 (提供了更新缓存策略的机会)。并可以处理功能性的事件 fetch (请求)、sync (后台同步)、push (推送)。
    • 废弃状态 ( redundant ):这个状态表示一个 Service Worker 的生命周期结束。

    关键方法

    • self.skipWaiting():表示强制当前处在 waiting 状态的 Service Worker 进入 activate 状态
    • event.waitUntil():传入一个 Promise 为参数,等到该 Promise 为 resolve 状态为止。
    • self.clients.claim():在 activate 事件回调中执行该方法表示取得页面的控制权, 这样之后打开页面都会使用版本更新的缓存。旧的 Service Worker 脚本不再控制着页面,之后会被停止。

    关键步骤:

    1、注册serviceWorker

    window.addEventListener('load', async () => {
        if ('serviceWorker' in navigator) {
            let registration = await navigator.serviceWorker.register('/sw.js');        
        }
    });
    

    只要sw.js  中的内容发生改变,就会重新注册serviceWorker

    2、注册监听函数

    创建一个sw.js文件

    self.addEventListener('fetch',(e)=>{
        console.log(e.request.url); // 拦截请求
    });
    
    self.addEventListener('install',(e)=>{   只有第一次安装才执行该回调
        e.waitUntil(skipWaiting()); // 跳过等待,直接激活
    });
    
    self.addEventListener('activate',(e)=>{    只有第一次激活才执行该回调
        e.waitUntil(self.clients.claim()); // 让serviceWorker拥有控制权
    })
    
    self指向的是当前浏览器进程
    

    以上三个步骤是关键

    3、缓存静态资源

    安装时将缓存列表进行缓存操作

    const CACHE_NAME = 'cache_v' + 1;
    const CACHE_LIST = [
        '/',
        '/index.css',
        '/main.js',
        '/api/list',
        '/index.html',
    ];
    async function preCache(){
        let cache = await caches.open(CACHE_NAME);
        await cache.addAll(CACHE_LIST);
        await self.skipWaiting();
    }
    self.addEventListener('install',(e)=>{
        e.waitUntil(preCache()); // 跳过等待,直接激活
    });
    

    激活后删除无用的缓存

    async function clearCache(){
        self.clients.claim();
        let keys = await caches.keys();
        await Promise.all(keys.map(key=>{
            if(key !== CACHE_NAME){
                return caches.delete(key);
            }
        }));
    }
    
    self.addEventListener('activate',(e)=>{
        e.waitUntil(clearCache()); // 让serviceWorker拥有控制权
    });
    

    4、离线使用缓存

    self.addEventListener('fetch',(e)=>{
        let url = new URL(e.request.url);
        if(url.origin !== self.origin){ // 静态资源走浏览器缓存
            return;
        }
        e.respondWith(
            fetch(e.request).catch(err=>{
                return caches.match(e.request);
            })
        )
    });
    

    5、缓存策略

    • cachefirst 缓存优先
    • cacheonly 仅缓存
    • networkfirst 网络优先
    • networkonly 仅网络
    • StaleWhileRevalidate 从缓存取,用网络数据更新缓存

    深入了解PWA工作原理

    完整的sw.js如下:

    // 对资源进行离线缓存  seriveWorker 可以自定义缓存的内容
    const CACHE_NAME = 'cache_v' + 2;
    const CAHCE_LIST = [ // 列表越长 越容易出问题
        '/',
        '/index.html',
        '/main.js',
        '/index.css',
        '/api/list',
        '/manifest.json',
        '/icon.png'
    ];
    // 当断网时 我需要拦截请求, 使用缓存的结果 
    
    // 核心就是拦截请求
    async function fetchAndSave(request){
        let res = await fetch(request); // 数据流
        let cloneRes = res.clone(); // 为了保证不破坏原有的响应结果
        let cache = await caches.open(CACHE_NAME);
        await cache.put(request,cloneRes); // 用响应结果更新缓存
        return res;
    }
    self.addEventListener('fetch', (e) => {
        // 如果是静态资源 不做拦截
        let url = new URL(e.request.url);
        if(url.origin !== self.origin){
            return
        }
    
        // 缓存策略, 如果接口是不停的变化的 我们希望将数据更新到缓存中
        if(e.request.url.includes('/api')){
            return e.respondWith(
                fetchAndSave(e.request).catch(res => {
                     return caches.match(e.request);
                })
            )
        }
    
        // serviceWorker中不支持ajax, 但是支持fetch
    
        // 如果断网了, 抛出异常
        e.respondWith(
            fetch(e.request).catch(res => {
                 return caches.match(e.request);
            })
        )
    });
    
    
    // 当serviceWorker 安装时 需要跳过等待
    
    async function preCache() {
        let cache = await caches.open(CACHE_NAME); // 创建一个缓存空间
        await cache.addAll(CAHCE_LIST);
        await self.skipWaiting()
    }
    self.addEventListener('install', (e) => {
        // e.waitUtil表示等待promise执行完成
        // 预先将缓存列表的数据缓存起来
        e.waitUntil(preCache())
    })
    async function clearCache() {
        let keys = await caches.keys();
        return Promise.all(keys.map(key => {
            if (key !== CACHE_NAME) {
                return caches.delete(key)
            }
        }))
    }
    self.addEventListener('activate', (e) => {
        e.waitUntil(Promise.all([clearCache(), clients.claim()])); 
        // 激活后立刻让serviceWorker拥有控制权
    })
    

     以上就是pwa的离线缓存的一个完整的过程,

    总结

    1、注册serviceWorker;2、注册监听函数,分为: 请求拦截、安装、激活。 拦截请求,将对应的数据,缓存起来,调用cache.put,**更新视图数据; **在安装的时候需要将缓存列表里对应的数据缓存起来,并且执行self.skipWaiting()跳过等待;激活的时候 执行self.clients.claim(), 让当前的serviceWorker获得控制权并删除无用的缓存;

    在实际项目项目开发中,我们只需要在webpack中配置workbox-webpack-plugin  这个插件

    new WorkboxPlugin.GenerateSW({ clientsClaim: true, skipWaiting: true })
    

    关于pwa服务端消息推送的原理,未完待续, 先不在这里阐述了。


    起源地下载网 » 深入了解PWA工作原理

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元