带您一步一步开发一个PWA技术的web app离线应用

带您一步一步开发一个PWA技术的web app离线应用

带您一步一步开发一个<a href='/tag/pwa.html'>PWA</a>技术的web app离线应用

一、PWA是什么?

随着app的泛滥,技术人员在创求一种新的技术让app开发更快,更新更快,成本更低,他们看上了html5,但是html5在制作web app的过程中有以下问题:


1、留白时间过长。移动端网络非常不稳定,经常会出现弱网环境,这样会导致资源加载速度非常慢,留白时间相对原生会慢很多。

2、没网络就没响应,不具备离线能力,以前只有App能(大量缓冲优化),h5的缓存用的极少。因为资源都在线上服务器,每次访问H5的页面强烈依赖网络,原生因为资源都在应用包里面,就算断网也会给一个相对友好的展示界面和用户提醒。

3、无法全屏访问。H5绝大部分都是跟浏览打交道,但是各大浏览器厂商都会有一个讨厌的头部和一个讨厌的尾部,导致用户的可视区域大大被压缩。原生大家都知道可视区域随意控制。

4、不像APP一样能进行消息推送。

5、手机桌面入口不够便捷,没有自己的启动图标,每次都需要输入网址或者依靠搜索引擎引流。

如果解决以上问题,那么h5将是是远程app开发的一种颠覆,2017年谷歌退出了PWA技术。

PWA全称progressive web app,其核心主要有3块,Manifest、Service Workers和storage

Service Workers是浏览器在后台独立于网页运行的脚本,它打开了通向不需要网页或用户交互的功能的大门。这个 API 之所以令人兴奋,是因为它可以支持离线体验,让开发者能够全面控制这一体验。

Service Workers要点:

它是一种 JavaScript 工作线程,无法直接访问 DOM。 服务工作线程通过响应 postMessage 接口发送的消息来与其控制的页面通信,页面可在必要时对 DOM 执行操作。

服务工作线程是一种可编程网络代理,让您能够控制页面所发送网络请求的处理方式,他可以拦截浏览器请求,将资源缓存在浏览器中,离线模式下任然可以访问资源,非常方便,看看下图

带您一步一步开发一个<a href='/tag/pwa.html'>PWA</a>技术的web app离线应用


带您一步一步开发一个<a href='/tag/pwa.html'>PWA</a>技术的web app离线应用



要安装服务工作线程,您需要通过在页面中对其进行注册来启动安装。 这将告诉浏览器服务工作线程 JavaScript 文件的位置。

带您一步一步开发一个<a href='/tag/pwa.html'>PWA</a>技术的web app离线应用

二、bfwstudio中创建一个PWA应用

注意,pwa的应用必须在https或localhost本地下才能执行,本文使用在线webide开发工具bfwstudio,在线使用https进行开发

1、注册一个bfw的社区账号,http://www.bfw.wiki/

2、登录后打开bfwstudio for html,http://studio.bfw.wiki/Studio/Open/lang/html.html

3、新建一个空白项目,取名pwademo

带您一步一步开发一个<a href='/tag/pwa.html'>PWA</a>技术的web app离线应用

4、首先在根目录下新建一个serviceworker.js,插入一下代码

const version = 'offline-cache-v2'

// Serverice Worker 安装成功后触发该事件
self.addEventListener('install', function (event) {
    // sw.js 有更新,立即生效
    event.waitUntil(self.skipWaiting());
});

// sw.js 有更新时触发该事件
self.addEventListener('activate', function (event) {
    event.waitUntil(
        Promise.all([
            // 更新客户端
            self.clients.claim(),

            // 删除旧版本的缓存对象
            caches.keys().then(function (cacheList) {
                return Promise.all(
                    cacheList.map(function (cacheName) {
                        if (cacheName !== version) {
                            return caches.delete(cacheName);
                        }
                    })
                );
            })
        ])
    );
});

// 网页发送请求触发该事件
self.addEventListener('fetch', function (event) {
    
    console.log('url is', event.request.url)

    event.respondWith(
        caches.match(event.request).then(function (response) {
            // 如果 Service Worker 有自己的返回,就直接返回,减少一次 http 请求
            if (response) {
                return response;
            }

            // 如果 service worker 没有返回,那就得直接请求真实远程服务
            var request = event.request.clone(); // 把原始请求拷过来
            return fetch(request).then(function (httpRes) {
                // 请求失败了,直接返回失败的结果就好了。。
                if (!httpRes || httpRes.status !== 200) {
                    return httpRes;
                }

                // 请求成功的话,将请求缓存起来。
                var responseClone = httpRes.clone();
                caches.open(version).then(function (cache) {
                    cache.put(event.request, responseClone);
                });

                return httpRes;
            });
        })
    );
})

这个js文件负责监听 fetch 事件,拦截请求;对缓存做处理,请求再缓存中直接返回,不在缓存中则请求服务端,将响应的资源再缓存中备份一份。

5、在项目根目录下新建一个index.html文件,插入以下代码

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <link href="css/index.css" rel="stylesheet">
</head>
<body>
    <img src="images/banana.png" />
    <script src="js/index.js"></script>
    <script>
        if ('serviceWorker' in navigator) {
            window.addEventListener('load', function() {
                navigator.serviceWorker.register('serviceworker.js').then(function(registration) {
                    // Registration was successful
                    console.log('ServiceWorker registration successful with scope: ', registration.scope);
                }, function(err) {
                    // registration failed :(
                    console.log('ServiceWorker registration failed: ', err);
                });
            });
        }
        
    </script>
</body>
</html>

'serviceWorker' in navigator 首先判断浏览器是否支持 Service Worker;
navigator.serviceWorker.register('serviceworker.js'') 这行代码即是注册 Service Worker,参数 serviceworker.js 是上面一步编写的js 文件,名字可以自定义,service worker 只要是拦截 http 请求,对资源进行缓存操作。

6、打开https预览按钮,在bfwstudio的预览->https菜单中

带您一步一步开发一个<a href='/tag/pwa.html'>PWA</a>技术的web app离线应用

8、点击预览->新窗口预览,打开开发者工具,切换到application标签,我们看到我们的serviceworker启动成功了

带您一步一步开发一个<a href='/tag/pwa.html'>PWA</a>技术的web app离线应用

我们看看clear strorage选项卡

带您一步一步开发一个<a href='/tag/pwa.html'>PWA</a>技术的web app离线应用

切换到Cache选项卡

带您一步一步开发一个<a href='/tag/pwa.html'>PWA</a>技术的web app离线应用

我们可以看到那些资源被缓存了

我们现在试试离线方式能不能打开网页,切换到service workers选项卡,勾线offline离线访问模式

带您一步一步开发一个<a href='/tag/pwa.html'>PWA</a>技术的web app离线应用

然后你刷新一下刚才页面试试,是不是任然可以访问

打开网络network选项卡

带您一步一步开发一个<a href='/tag/pwa.html'>PWA</a>技术的web app离线应用

可以看到离线模式下资源的请求访问都是通过serviceworker来缓存响应的,statuscode任然是200

那么如果我们不缓存所有的资源,有些资源我们不需要缓存,那么只要改下代码就好了

// 白名单
const whiteUrls = [
    'jquery.js',
    'vue.js',
]

self.addEventListener('fetch', function (event) {
    event.respondWith(
        caches.match(event.request).then(function (cacheRes) {
            // .......

            return fetch(request).then(function (httpRes) {
                if (!httpRes || httpRes.status !== 200) {
                    return httpRes;
                }

                // 符合白名单的才放到缓存中,其它的正常请求
                if (whiteUrls.findIndex(event.request.url) !== -1) {
                    var responseClone = httpRes.clone();
                    caches.open('offline-cache-v1').then(function (cache) {
                        cache.put(event.request, responseClone);
                    });
                }
                
                return httpRes;
            });
        })
    );
})

本项目所有的代码存在bfwstudio webide上,在线打开即可开发,地址http://studio.bfw.wiki/Studio/Open.html?projectid=15874608911762310073

{{collectdata}}

网友评论0