PWA 学习小结

  1. 创建主屏幕入口

<link rel="manifest" href="manifest.json" />

manifest.json文件大概是这样的

{
  "name": "微博Lite",
  "short_name": "微s博",
  "description": "随时随地分享新鲜事",
  "icons": [{
      "src": "https://luoyongjie.cn/lab/pwa/icon/weibologo.png",
      "sizes": "606x606",
      "type": "image/png"
    }],
  "start_url": "/static/pwa/",
  "scope": "/static/pwa/",
  "display": "browser",
  "orientation": "portrait",
  "background_color": "#F3F3F3",
  "theme_color": "#F3F3F3",
  "related_applications": [],
  "prefer_related_applications": false
}

 

这里我是抄m.weibo.com/beta的manifest.json

内容太多,不一一称述,无非就是设置logo图片,访问地址,app打开的屏幕,是否显示导航栏,就像一个HirdApp一样

这里说一下关键问题

  1. 设置主入口的交互是如何的?

image.png

image.png

点击添加之后就会在桌面出现icon入口了

感觉这样的交互,用户几乎不会这样做,太鸡肋了,那么有没有可以用js控制提醒用户是否需要添加到主屏幕的方式呢?没有。。。。。

于是我带着怀疑的态度,尝试了

chrome IOS版本 居然没有开放这个操作

chrome Android版 操作有了,确认添加到主屏幕了,但是屏幕就是找不到icon入口

2.server worker 离线缓存

入口文件处载入如下代码:

<script>
function registerServiceWorker(){
    return navigator.serviceWorker.register('./sw.js').then(registration => {
        alert('注册成功');
        return registration;
    })
    .catch(err => {
        alert('注册失败', err);
    });
}
window.onload = function () {
    if (!('serviceWorker' in navigator)) {
        return;
    }
    registerServiceWorker()
}
</script>
const CURCACHE = 'CURCACHE_test_1'
const RUNTIME = 'runtime';
var CURCACHE_URLS = [
  '/lab/pwa/index.html',
  '/lab/pwa/main.css'
];
self.addEventListener('install',e=>{
    e.waitUntil(
      //存储缓存路径对应的资源
        caches.open(CURCACHE).then(cache=>{
            cache.addAll(CURCACHE_URLS)
        }).then(
            self.skipWaiting()
        )
    )
})
 
 
   
  //代理请求,使用缓存,请求发送之前
  self.addEventListener('fetch', e => {
    e.respondWith(
      //缓存是否匹配 
      caches.match(e.request).then(function(response) {
        if (response != null) {
          //命中缓存返回缓存,结束请求
          return response
        }
        //未命中缓存,正常请求
        return fetch(e.request.url)
      })
    )
  });

 image.png

只要caches设置了缓存,就能在离线状态下访问页面,同时可以在sw中控制缓存哪些内容。

sw.js文件每次都会请求服务器获取最新的server worker文件

注意:如果浏览器本身对sw.js进行缓存的话,也不会得到最新代码,所有代码会变成死代码,无法更新。所以对sw文件最好配置成cache-control: no-cache

经过测试,ios safair,chrome 都支持,但是微信页面不支持 navigator.serviceWorker == undefined,估计是微信为了推广小程序,故意封掉的

3.消息推送

https://firebase.google.com/docs/web/setup

按照这里的教程可以利用firebase的安装包,可以做出一个消息推送的demo。不过必需要翻墙。

实际体验如下:

需要在服务器环境模拟一个请求到google的推送服务器

比如

curl -X POST -H "Authorization: key=YOUR-SERVER-KEY" -H "Content-Type: application/json" -d '{
  "notification": {
    "title": "Portugal vs. Denmark",
    "body": "5 to 1",
    "icon": "firebase-logo.png",
    "click_action": "http://localhost:8081"
  },
  "to": "YOUR-IID-TOKEN"
}' "https://fcm.googleapis.com/fcm/send"

然后在安装好firebaseSDK的页面上打开

电脑端:焦点在当前窗口的话,会即使调用当前页面的js messaging.onMessage 方法收到msg

            如果焦点不在当前窗口,则启动ServerWorker后台的self.registration.showNotification

        image.png

        浏览器就会在系统层发出一个消息通知。

        如果断网的情况下,等到网络恢复,这个消息会自动弹出来

手机端:ios的 微信,safair,chrome 完全不兼容

            android的chrome 比较乐观

            1541054402791967.png

           可是只是收到几次,突然就不行了,不知道是不是系统做了限制,卸载再安装又可以收到几次。

            如果把chrome进程杀掉,也是收不到推送的,这一点挺坑的。

          如果要关注兼容问题,可随时关注 

            https://caniuse.com/#search=Push%20API

            https://caniuse.com/#search=Notifications 这个API的兼容情况

小结:

注意以上所有环境都需要在https或localhost或127.0.0.1进行,因为server worker规定了只能在这些调节下才能开启。如果是用手机做测试,那你必需拥有一个https的站点才能测试,这一点非常坑,于是我把站点升级成https了。

实际情况:PWA最重要的一点是离线缓存,(首屏入口和消息推送用的场景不是刚需)提高用户首屏加载体验,可是这么关键的一点,居然被微信封杀了,在中国h5的流量几乎都在微信,场景限制非常大。如果某个h5不是在wx推广的,我觉得pwa离线缓存是提高用户体验的利器。

vue响应式源码实现

png

const Observer = function(data) {
  // 循环修改为每个属性添加get set
  for (let key in data) {
    reactive(data, key);
  }
}
const Dep = function() {
  const self = this;
  //这里可以用数组,就能实现同一个data,观察多个vue实例
  this.depend = ()=>{
    if (Dep.target) {
	  this.sub = Dep.target
	}
  }
  this.notify = ()=>{
	  this.sub.update();
  }
}
const Watcher = function(vm, fn) {
  //这样的写法是为了把target传到dep实例里面,当然也可以不挂载到dep,但要有一个地方暂存起来
  Dep.target = this;
  this.update = function() {
    console.log('in watcher update');
    fn();
  }
  //初始化运行一次render函数
  fn();
  Dep.target = null;
}
const reactive = function(obj, key) {
	const dep = new Dep();
	let val = obj[key];
	Object.defineProperty(obj, key, {
		enumerable: true,
		configurable: true,
		get() {
		  console.log('in get');
		  dep.depend();
		  return val;
		},
		set(newVal) {
		  if (newVal === val) {
			return;
		  }
		  val = newVal;
		  dep.notify();
		}
	});
}
const Vue = function (options) {
	this._data = options.data.call(this);
	//设置data的get,set
	Observer(this._data);
	this.mount = (id)=>{
		this.dom = document.querySelector(id);
		new Watcher(this, this.render);
	}
	//渲染函数,这里主要是触发get方法
	this.render = ()=>{
		//这里vue应该会有虚拟dom的算法实现
		this.dom.innerHTML = `<div>${this._data.text}</div>`;
	}
}
const vue = new Vue({
  data() {
    return {
      text: 'hello world'
    };
  }
})
vue.mount('#app');
vue._data.text = '123';

首先看一下主入口 Vue,包含 Observer, mount, render 三个方法

其中 Observer 的作用是为了获取data中的每一个get,set操作,以达到监听数据变化的手段(可用ES6的Proxy实现)

Watcher函数有2个作用,第1个是在程序首次运行的时候,用于触发Observer中的get方法,收集依赖,并把当前watcher的实例绑入到dep中。

                                    第2个作用是,当数据被修改,也就是触发set操作的时候,通知对应的dep中的watch实例触发updata->render,达到页面重新渲染的目的。

render函数这里只是简单的一行代码,实际vue里面的实现应该是非常复杂的,包括 jsx,vue模版引擎,虚拟dom之类的实现

github地址: https://github.com/gaxking/vue-deeplearn 

内容安全策略( CSP )

CSP 通俗的来说就是可以根据header的字段,或者meta标签,限制页面的script,图片,video等外部自由的加载来源,以达到防止被XSS的目的

下面来实践一下:

header 可以设置:Content-Security-Policy

或者在

meta 设置 <meta http-equiv="Content-Security-Policy" content="default-src 'self'">

这里方便我做测试,就使用meta方式吧

常用设置如下:

1.所有内容均来自站点的同一个源

Content-Security-Policy: default-src 'self'  

[demo]

2.允许所有内容来自信任的域名,或指定域名

Content-Security-Policy: default-src 'self' *.baidu.com

[demo]

3.分别控制网页的图片,音频或视频,脚本

Content-Security-Policy: default-src 'self'; img-src *; media-src media1.com media2.com; script-src luoyongjie.cn

[demo]

4.  Content-Security-Policy-Report-Only: /something/

这个属性只会产生警告不强制失败

[demo]

5.还能发送违法请求

Content-Security-Policy: default-src 'self'; report-uri http://reportcollector.example.com/collector.cgi

这里就不写demo了

常用图片格式对比分析

常用图片无非就以下4种

  1. PNG

  2. JPG(JPEG)

  3. GIF

  4. WebP

1.png分为png8 和 png24

png8 只支持2^8(256)种颜色,半透明和全透明

仔细看一下png8的妹子图片,颜色看起来确实不够鲜艳

image.png

这幅是我随便画的图片,图层设置50%半透明,但是png8 只支持 全透明和全不透明

image.png

png8 适合应用于颜色比较单一的logo,icon图片。

png24相比,支持半透明,色彩也显示得更加丰富,但是相应的图片大小更大,适合应用于摄影作品

jpg也可以叫jpeg,色彩支持24bit,同png24一样能显示丰富的色彩,但是不支持半透明,但是图片体积比较小,适用于网络上显示比较丰富的图片


在图片经过编辑保存之后,jpg图片会逐渐失真,png不会。

原因是jpg采用的是有损压缩,png采用的是无损压缩。


gif:gif可以支持动画,类似于png8的布尔透明类型,只有全透明跟全不透明,没有半透明,是无损耗的图像格式。

webP:是有损压缩,

WebP:可以选择区别无损和有损压缩

在有损压缩情况下,相比JPEG减少25%~34%的大小,有损WebP也支持透明通道,大小通常约为对应PNG的1/3

在无损压缩情况下,相比PNG减少26%大小

于gif相比支持动画,动态WebP相比GIF支持更丰富的色彩,并且也占用更小空间,更适应移动网络的动图播放。

1539156399134726.png

唯一的缺点是,技术实现的成本比较高,而且苹果和ie对它完全不支持