Angular 学习笔记 ( PWA + App Shell )

书史足自悦,安用勤与劬。这篇文章主要讲述Angular 学习笔记 ( PWA + App Shell )相关的知识,希望能为你提供帮助。
更新 : 2019-06-29 
自从 pwa 一推出开始玩了一把,一直到现在才真的有项目需求...
重新整理了一下. 
https://angular.io/guide/service-worker-getting-started
跟着 get start 走,基本上 3 个 command 就搞定了. 真实方便呢
下面提一提需要留意的东西 
Hash Mismatch 问题
如果我们用 live server 来测试, 你会发现 pwa 跑不起来, 
如果我们用 iis 也很有可能出现这种情况 
其中的关键就是, ng build 的时候 angular 会拿 index.html 来 hash 
然后以这个判断内容是否有修改. 这个是很常用的技术. 比如  html content security policy 在保护 script 执行时也有对比 hash 的概念
live server 和 iis 或者 cdn 经常会帮我们 minify html, 所以会生成的 hash 与 ng build 的时候不吻合 (ng build 的时候没有 minify)
官方的例子很巧妙的使用了 http-server 避开了这件事儿... 也留些了坑 /.\\
https://github.com/angular/angular/issues/21636  (很多人踩进去了...)
当然你也不能怪谁啦... 
期望以后 angular 会帮我们 minify 咯
https://github.com/angular/angular-cli/issues/11360
https://github.com/angular/angular/issues/23613
https://www.willpeavy.com/tools/minifier/
目前我的解决方法就是手动提前 minify index.html 才 ng build. 
如果是开发阶段,还是用 http-server 吧. 
npm i g http-server 
http-server -p 4200 -a localhost  ./dist/project-name  -o http://localhost:4200/index.html
 
另一个要注意的是 cache  dataGroups  机制. 
data groups 通常用来 cache api 返回的资料. 
它有 2 个 mode 
一个是  freshness,  另一个是 performance
先说  performance,它就好像一般的 js 的 cache 一样,有 cache 就不会去 server, 等到 max age 到为止. 
freshness 比较特别, 在 network 好的情况下, 它总是去 server 拿回来,然后存一份 cache 
当 offline 时它会马上使用 cache, 或者时 network 超时的时候它会立刻返回 cache,
然后继续等待 network 把资料拿回来后更新 cache, 注意它只是更新 cache 并不会通知 app 哦, 所以 app 这时候用的是之前 cache 的资料。
这个体验就不是很好了,但是一个请求是无法获得 2 个 response 的,如果拿了 cache 那么就获取不到新的了. 
我们也无法精准的判断 response 是 from cache or server. (最多只能判断 timeout 时间来猜测而已.)
一般上我们的需求是, offline 用 cache, online 用 server. 没有超不超时的概念. 
所以我通常的做法时 set 一个很长的超时, 如果用户不是 offline 而是超时的话也不会用 cache, 而是报错之类的处理. 
 
还有一个好的功能是, by pass 

To bypass the service worker you can set ngsw-bypass as a request header, or as a query parameter. (The value of the header or query parameter is ignored and can be empty or omitted.)

在请求 header 加上 ngsw-bypass 就可以 skip 掉 service worker 的 fetch 了. 
 
最后在说说 notification click 的问题.
https://github.com/angular/angular/issues/20956
这个 issue 被关掉了, ng 提供了一个监听的方法
this.swPush.notificationClicks.subscribe(v => { console.log(\'done\'); });

这个只有在 app 打开着的状态下才有用, ng 并没有实现 action open window ...
如果我们要实现依然需要修改  ngsw-worker.js
真的是啊....
https://github.com/angular/angular/issues/21197
也没有给我们好的方式去扩展 
目前用到一个做法是 ..
https://medium.com/@smarth55/extending-the-angular-cli-service-worker-44bfc205894c
//my-service-worker.js self.addEventListener(\'notificationclick\', (event) => { console.log(\'notification clicked!\') }); //app.module.ts ServiceWorkerModule.register(\'my-service-worker.js\', { enabled: environment.production })"assets": { ..., "src/my-service-worker.js" }//my-service-worker.js importScripts(\'./ngsw-worker.js\'); self.addEventListener(\'notificationclick\', (event) => { console.log(\'notification clicked!\') });

做法就是 extends + override 咯。这样就可以实现了咯
一个好得体验应该是这样子,到用户在线时,我们不应该发 notification,而应该用 web socket 去同步资料
只有当用户不在线时,我们才需要通过 notification... 不然你想,用户就在屏幕前,notification 一直发... 人家会不喜欢嘛。
 
 
PWA (Progressive Web Apps) 是未来网页设计的方向. 渐进式网站.
Angular v5 开始支持 pwa 网站 (所谓支持意思是说有一些 build in 的方法和规范去实现它) 。
就目前来说 pwa 有几个特点 : 
1.https 
2.Service work 
3.Cache API
4.拦截 Fetch (任何游览器发出的请求, 包括 index.html)
5.Push API
6.Share API
 
主要的用途是 : 
1. offline view (通过 service work + cache + 拦截 fetch 实现)
2. push notification (通过 service work + Push API + Notification API 实现)
3. AMP 网站预加载 service-work.js (在 amp page 出发 service worker 预加载整个页面需要的 html,js.css)
 
参考 : 
https://blog.angular-university.io/service-workers/
https://blog.angular-university.io/angular-service-worker/
实现我就不说了,人家已经是 step by step 了. 我就讲一些重点吧.
service work 比较复杂的地方是 life cycle. 
当你访问一个网站后 www.domain.com 
当页面渲染好后, ng 会找到一个好的时间点去 register service worker 也就是加载 "/ngsw-worker.js".
ng 有自己的方式(比如对比文件的 hash 等等)去管理 life cycle (如果你知道怎么自己实现 service worker 的话,你会发现 ng 完全自己控制了 life cycle 而没有使用 default 的)
service work 开启后, 就是一般的预加载 css, js, html 等等. 然后统统都会 cache 起来. 
完成后, 你 offline 就可以看到效果了. 你 refresh 的话会发现所有的请求都是从 cache 返回的,包括 index.html 
连 index.html 都 cache 起来了,那要怎样更新网站呢 ? 
每一次更新, ng 在 cli build 的时候都会生产一个 hash 放进 ngsw-worker.js,
网站每一次刷新虽然会先使用 cache 版本,但是它也会马上去加载 ngsw-worker.js 然后进行判断看 hash 是否一样。
如果发现有新的 js,css 那么就会去加载,等到下一次 refresh 就会使用新版本了. 如果你希望用户马上使用新版本, ng 也开放了一个 API
可以通过 subscribe 的方式监听这个 update event, 然后可以 alert 用户叫用户马上 refresh.
所以流程是  cache first -> check update -> notify user and update now Or wait for user next refresh 
我建议在网站比较稳定后才设置 service work, 而
而且网页必须向后兼容, 或至少要有错误处理在版本过久的情况下。
因为不管怎样,用户一定会先获取到 cache 的版本,如果你的 cache 版本运行失败(比如你的 Ajax response 已经换了, 而之前的 js 版本无法处理, 甚至 error, 这样用户可能非常的难升级到新版本,而且体验也会很糟. 所以要用 pwa 要注意这个哦)
 
除了 cache, ng 也封装了 push notification。
之前写过一篇关于 push 的原生实现. 
http://www.cnblogs.com/keatkeat/p/7503615.html 
ng 的实现看这个 
https://medium.com/google-developer-experts/a-new-angular-service-worker-creating-automatic-progressive-web-apps-part-2-practice-3221471269a1
目前还不支持 notification click 时间,这个还蛮糟糕的,非常重要的功能丫。怎么会没有实现呢 ?  https://github.com/angular/angular/issues/20956
而且也没有扩展的方式,如果硬要就需要直接改  ngsw-worker.js  源码了。
 
最后说说 App-shell 
这个和 skeleton 类似的概念, 取代单调的 loading bar.
step by step :  https://blog.angular-university.io/angular-app-shell/
ng 的实现手法是通过 cli build 时运行 server render, 然后把渲染好的 skeleton page 插入到 index.html. 
【Angular 学习笔记 ( PWA + App Shell )】 

    推荐阅读