JavaScript轻应用PWA实践全过程( 二 )


Worker 激活

JavaScript轻应用PWA实践全过程

文章插图
 
激活之后,我们做了3件事:
a. 更新所有的同源客户端的service_worker.js,即使它没有刷新页面 。
b.清除非当前最新版本的cache 。
c. 把首页与离线页面(根据自己的需要)进入立即缓存,如果不这么做的话,因为激活阶段(第1次打开页面)还没到达,Worker还没有开始做cache的工作,页面已经打开了,这时是没有离线缓存的,第2次打开页面时没有离线cache,但这时页面会缓存下来,只有第3次才开始能取到离线cache,而上述这么做,第2次进来即可以拿到离线cache的首页 。offline.html则是离线状态下的提示页,否则用户不知道可以离线缓存,就直接不再使用APP了 。
Cache Storage 离线缓存
JavaScript轻应用PWA实践全过程

文章插图
 
注意点:
a. Cache Storage与我们常说的浏览器缓存(Http Cache)有相似之处,即对整个请求/文件缓存 。又有不同之处,它可永久保存,可离线使用 。在在Chrome devTools -> Application -> Cache -> Cache Storage中可以查看 。
b. fech事件可以拦截HTTPS的请求,进行缓存,但下次请求时如果发现已经缓存过,则直接返回缓存中的HTTPS Response,不过上述代码没有这么做,因为博客页面非常小,为了追求页面最新,只有当离线时才使用缓存,这种做法其实是偏离了离线缓存减小服务器压力的的初衷 。不过离线缓存与时时更新是矛盾的,取决于业务怎么权衡了 。
c. 请求都是clone之后才缓存,因为请求的状态是变化的,如果直接保存,可能不是当时的结果 。
d. 只有Get请求才缓存,否则会报错,毕竟像Post/Put/Delete之类的离线缓存也没有意义 。这里开发者可以自己定义规则 。
e. 离线提示页是在这里拦截而实现的 。
f. 为了保证顺利升级,我在缓存中设置的升文件“manifest.json”、“service_worker.js”,“service_worker_before.js”是不做离线缓存的 。
Web Push 消息推送
Web Push的过程比较复杂,因为它涉及到4个端:
JavaScript轻应用PWA实践全过程

文章插图
 
首先先列出简化的9个步骤:
a. 业务服务端生成公钥与私钥,并把公钥给网页客户端
b. 网页客户端需要支持PushManager前提下,然后请求用户授权通知
c. b的基础上,网页客户端把公钥转成Uint8Array
d. 网页客户端向推送服务端发起订阅,如果成功,会得到推送服务器返回的订阅信息
e. 网页客户端把订阅信息发给业务服务端
f. 业务服务端保留该订阅信息
g. 业务服务端拿着订阅列表、公钥私钥、把想要推送的信息发送给推送服务端
h. 推送服务端拿到推送信息,解析后发送给Service Worker端
i. Service Worker监听到信息,使用Notification推送给用户
除了四个端之间有各种交互,还有各种加密比较麻烦外,关于推送服务器文档少、不便于调试、兼容性不好也是个问题 。
关于Web Push的php后端实现
本博客后端使用的PHP,相关教程较少,所幸已经开源的组件可用https://github.com/web-push-libs/web-push-php 。
安装minishlink/web-push
yum install php-gmpcomposer require minishlink/web-push可是安装报错:
The following exception is caused by a lack of memory or swap, or not having swap configured
Check https:// getcomposer 。org/doc/articles/troubleshooting.md#proc-open-fork-failed-errors for details
PHP Warning: proc_open(): fork failed - Cannot allocate memory in phar:// /usr/local/bin/composer/vendor/symfony/console/Application.php on line 952
Warning: proc_open(): fork failed - Cannot allocate memory in phar:// /usr/local/bin/composer/vendor/symfony/console/Application.php on line 952
[ErrorException]
proc_open(): fork failed - Cannot allocate memory
内存问题,修改后OK
/bin/dd if=/dev/zero of=/var/swap.1 bs=1M count=256/sbin/mkswap /var/swap.1/sbin/swapon /var/swap.1a.生成公钥私钥
use MinishlinkWebPushVAPID;echo var_dump(VAPID::createVapidKeys());f. 业务服务端保留该订阅信息

g. 业务服务端拿着订阅列表、公钥私钥、把想要推送的信息发送给推送服务端
public function push_mess(Request $request){ $title = $request->input('title'); $body = $request->input('body'); $href = https://www.isolves.com/it/cxkf/yy/js/2019-08-08/$request->input('href'); $noticeObj = new stdClass(); $noticeObj->title = $title; $noticeObj->body = $body; $noticeObj->href = $href; $noticeObj->icon = "/static/dist/image/common/favicon.ico"; $noticeObj->badge = "/static/dist/image/common/favicon.ico"; $auth = array( 'VAPID' => array( 'subject' => 'https://www.boatsky.com/', 'publicKey' => 'BGMKbiifiHo5zKaK+gQ=', 'privateKey' => 'FjGJbNeg=', ), ); $webPush = new WebPush($auth); $subList = DB::table(SUBSCRIPTION_TABLE_NAME) ->get(); foreach($subList as $sub){ $subscription = Subscription::create(array( 'endpoint'=> $sub->endpoint, 'publicKey'=> $sub->public_key, 'authToken'=> $sub->auth_token, 'contentEncoding'=> $sub->content_encoding ), true); $res = $webPush->sendNotification( $subscription, json_encode($noticeObj) ); } // handle eventual errors here, and remove the subscription from your server if it is expired $pushResult = ''; foreach ($webPush->flush() as $report) { $endpoint = $report->getRequest()->getUri()->__toString(); if ($report->isSuccess()) { $pushResult = $pushResult . "[successfully] -- {$endpoint}.


推荐阅读