@xiaoqq
2018-03-14T07:41:08.000000Z
字数 4614
阅读 1480
PWA
PWA正如火如荼地发展起来了,我对国内使用到的大厂网站做了一个汇总:
天猫超市: https://chaoshi.m.tmall.com/?_ig=shoumao&scm=1003.64.201704071.OTHER_1511581550776_1699747&pos=2&acm=201704071.1003.64.1699747&spm=a1z60.8521859.icon.2&ttid=201200%40tmall_iphone_7.2.1&deviceId=50E1DC42-9319-4D3B-868E-691B170FB06C
天猫超市生鲜店的比较赞,https://chaoshi.m.tmall.com/sw.htm
微博beta版:https://m.weibo.cn/beta
花呗网页版:https://render.alipay.com/p/h5/huabei/www/index.html
使用场景较少,仅仅只限做测试用,玩具级别引用。
例如:https://render.alipay.com/p/h5/huabei/www/swHtmlTest.html
饿了么:https://h5.ele.me/msite/
饿了么是国内比较早一批尝试PWA的厂家,但是给人感觉却技术不是特别好,网站也不稳定,
https://h5.ele.me/sw.js
百度Lavas框架:https://lavas.baidu.com/mip/guide/vue/doc/vue/webpack/sw-register-webpack-plugin
思路有两种,a) 在SW通过fetch方法发送,这种方法限制太多
const sendMars = function(seed, data) {
function fixHref(href) {
if (href.indexOf("?") < 0) {
href += "?"
}
if (href.indexOf("vipruid") < 0) {
href += "&vipruid="
}
return href
}
var url = "https://mar.vip.com/b?" + "one=" + encodeURIComponent(seed) + "&data_mars=" + (!!data ? encodeURIComponent(JSON.stringify(data)) : "") + "&url=" + escape(fixHref(self.location.href)) + "&bv=" + escape(navigator.userAgent.toLowerCase()) + "&r=" + Math.random();
if (url.length <= 6144) {
var req = new Request(url, {
mode:'no-cors',
method: "GET",
headers: {
"Content-Type": "image/png",
},
credentials: "include"
});
fetch(req);
}
}
b) 通过client.postMessage发送消息给JS,发送埋点
function sendMars(seed, data) {
self.clients.matchAll()
.then(function (clients) {
if(clients && clients.length) {
clients.forEach(function (client) {
client.postMessage({
name: 'sw.mars',
value: {
seed: seed,
data: data
}
});
})
}
});
}
navigator.serviceWorker.addEventListener('message', e => {
// service-worker.js 如果更新成功会 postMessage 给页面,内容为 'sw.update'
if(e.data.name == 'sw.mars') {
if(window.Mar) {
window.Mar.Seed.request("mars_sead", "click", e.data.value.seed.toString(), e.data.value.data);
}else {
sendMars(e.data.value.seed.toString(), e.data.value.data);
}
}
})
1) 后端提供接口,每次请求sw.js之前都必须要请求接口;
//调用后端降级接口
fetch(`//m.vip.com/api/collection/pwa/downgrade`).then(function(response) {
return response.json();
}).then(result => {
if(result.success && !result.results._defaultResult.downgrade) {
log('注册SW');
registerSw()
}else {
log('降级SW');
unRegisterSw();
}
});
2)利用html不会被缓存的特性,使用Html + iframe标签,在html中装载JSON数据,通过运营后台更改
var entryNode = document.querySelector('#entryChunks');
entryNode.innerHTML = '<iframe id="entryIframe" style="display:none;" src="/pages/json/entrychunks.html?t=' + (new Date).getTime() + '"></iframe>';
var iframe = document.querySelector('#entryIframe');
iframe.onload = function() {
var doc = iframe.contentDocument;
var html = doc.body.innerHTML;
var result = JSON.parse(html);
if(result.success && !result.results._defaultResult.downgrade) {
log('注册SW');
registerSw()
}else {
log('降级SW');
unRegisterSw();
}
}
3) 监听SW的push事件
this.addEventListener('push', function(e){
console.log("receive push message::", e.data.text());
// u4 1.0不支持json()
var data;
try{
data = e.data.json ? e.data.json() : JSON.parse(e.data.text());
// alipay下需要多一层message,并且message里的东西是一个字符串
if(data.message){
data = JSON.parse(data.message);
}
}catch(ex){}
if(!data || !data.action){
return;
}
if(data.action === 'update'){
// 当下发的版本大于当前版本时才更新
// U4 1.0不支持update,降级为清楚本地缓存
if(data.version && compareVer(data.version, VERSION)){
if(this.registration.update){
this.registration.update().then(function(){
log('19');
}, function(){
log('20');
});
}else{
caches.open(CACHE_NAME)
.then(function (cache) {
return cache.delete(FRESH_URL).then(function(){
return cache.add(FRESH_URL);
}).then(function(){
log('17');
}, function(){
log('18');
});
});
}
}else{
console.warn('push: current version is lastest')
}
log('25');
}else if(data.action === 'unregister'){
caches.delete(VERSION).then(function(){
self.registration.unregister().then(function(){
log('21');
}, function(){
log('22');
});
});
}else{
console.warn('push: this push message is not supported, ' + e.data.text())
}
//heartBeat();
})
service worker有一个非常重要的点就是sw.js每次必须请求最新的数据。如果html被service worker缓存,sw.js被浏览器缓存,那么页面的静态资源就永远不会请求服务器!所以一定要保证sw.js处于最新。sw.js但凡有一个字节的改变都会触发重新安装。
防止sw.js被缓存有三种做法
1) 在nginx设置sw.js的缓存头文件
location ~ \/sw\.js$ {
add_header Cache-Control no-store;
add_header Pragma no-cache;
}
2) 设置sw.js后缀为sw.html,并且将content-type改为application/x-javascript; charset=utf-8
由于html不会被缓存,所以每次都可以请求最新的数据。
location ~ sw\.html$ {
add_header Content-Type application/javascript;
}
3) 增加一个sw-register.js来加载sw.js,每次上线,保证sw.js的版本号也同时被刷新
//sw-register.js
window.onload = function () {
var script = document.createElement('script');
var firstScript = document.getElementsByTagName('script')[0];
script.type = 'text/javascript';
script.async = true;
script.src = '/sw-register.js?v=' + Date.now();
firstScript.parentNode.insertBefore(script, firstScript);
};
if (navigator.serviceWorker) {
navigator.serviceWorker.register('/service-worker.js?v=build的版本号').then(function () {
// balabala
});
}