浅析Service Worker
一、service worker是什么?
平常浏览器窗口中跑的页面运行的是主JavaScript线程,DOM和window全局变量都是可以访问的。
Service Worker是走的另外的线程,可以理解为在浏览器背后默默运行的一个线程,或者说是独立于当前页面的一段运行在浏览器后台进程里的脚本。
它脱离浏览器窗体,异步地运行在一个完全独立的上下文环境,不会对主线程造成阻塞。在service worker中,window以及DOM都是不能访问的,但可以使用self访问全局上下文。
二、service worker的作用?
1.离线缓存(重点)
2.消息推送(重点)
3.后台数据同步
4.响应来自其它源的资源请求,
5.集中接收计算成本高的数据更新,比如地理位置和陀螺仪信息,这样多个页面就可以利6.用同一组数据
7.在客户端进行CoffeeScript,LESS,CJS/AMD等模块编译和依赖管理(用于开发目的)
8.后台服务钩子
9.自定义模板用于特定URL模式
10.性能增强,比如预取用户可能需要的资源,比如相册中的后面数张图片
三、service worker的局限
1、https: Service Worker必须是https协议的,但本地环境下http://localhost或者http://127.0.0.1也可以的。
2、浏览器兼容性:

四、service worker的调试
1、chrome://serviceworker-internals

2、网页中Application


五、service worker的生命周期
Service Worker生命周期的反应: installing → installed → activating → activated
'install'用来缓存文件,'activate'用来缓存更新

六、service worker的用法
1、html中
if ('serviceWorker' in navigator) {
// 开始注册service workers
navigator.serviceWorker.register('./sw-demo-cache.js', {
scope: './'
}).then(function (registration) {
console.log('注册成功');
var serviceWorker;
if (registration.installing) {
serviceWorker = registration.installing;
console.log('安装installing');
} else if (registration.waiting) {
serviceWorker = registration.waiting;
console.log('等待waiting');
} else if (registration.active) {
serviceWorker = registration.active;
console.log('激活active');
}
console.log('=>serviceWorker:', serviceWorker);
if (serviceWorker) {
console.log(serviceWorker.state);
serviceWorker.addEventListener('statechange', function (e) {
console.log(' 状态变化为', e.target.state);
});
// 创建信道
var channel = new MessageChannel();
// port1留给自己
channel.port1.onmessage = e => {
console.log('main thread receive message...');
console.log(e);
}
console.log('给对方', window.RES_MAP);
// port2给对方
serviceWorker.postMessage(window.RES_MAP, [channel.port1]);
serviceWorker.addEventListener('statechange', function (e) {
// logState(e.target.state);
});
}
}).catch(function (error) {
console.log('注册没有成功');
});
} else {
console.log('不支持');
}
2、引进sw-demo-cache.js
// sw
self.addEventListener('message', ev => {
console.log('sw receive message..');
console.log(ev);
fileMap = ev.data.RES_MAP;
var arr1 = [].slice.call(fileMap); // ['a', 'b', 'c']
// 取main thread传来的port2
ev.ports[0].postMessage('Hi, hello too');
});
// var fs = require('fs');
// console.log(fs);
// 缓存
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open(VERSION).then(function(cache) {
return cache.addAll([
'./index.html',
]);
})
);
});
// 缓存更新
self.addEventListener('activate', function(event) {
console.log('two now ready to handle fetches!');
event.waitUntil(
caches.keys().then(function(cacheNames) {
return Promise.all(
cacheNames.map(function(cacheName) {
console.log('cacheName:', cacheName);
// 如果当前版本和缓存版本不一致
if (cacheName !== VERSION) {
return caches.delete(cacheName);
}
})
);
})
);
});
// 捕获请求并返回缓存数据
self.addEventListener('fetch', function (event) {
try{
event.respondWith(
caches.match(event.request).then(function(res){
if(res){
return res;
}
requestBackend(event);
})
)
} catch {
console.log(event);
}
});
function requestBackend(event){
var url = event.request.clone();
return fetch(url).then(function(res){
//if not a valid response send the error
if(!res || res.status !== 200 || res.type !== 'basic'){
return res;
}
var response = res.clone();
console.log('VERSION:', VERSION);
caches.open(VERSION).then(function(cache){
cache.put(event.request, response);
});
return res;
})
}
3、webapck中获取文件目录
引入第三个模块glob,递归获取打包后的文件目录
exports.resMap = function () {
var entryFiles = glob.sync(PAGE_PATH + '/*/*.js')
var map = {}
entryFiles.forEach((filePath) => {
var filename = filePath.substring(filePath.lastIndexOf('\/') + 1, filePath.lastIndexOf('.'))
map[filename] = filePath;
})
var entryFiles2 = glob.sync(PAGE_PATH2 + '/*')
var map2 = {}
findPath(entryFiles2, map2);
console.log('map2', map2);
return map2;
};
function findPath(entryFiles2, map2) {
entryFiles2.forEach(filePath => {
var filename = filePath.substring(filePath.lastIndexOf('/') + 1, filePath.lastIndexOf('.'));
if (filePath.indexOf('.') <= 0) {
let pathRes = path.resolve(__dirname, filePath);
let files = glob.sync(pathRes + '/*');
findPath(files, map2);
map2[filename] = filePath;
}
map2[filename] = filePath;
});
}
4、导出目录
通过webpack的DefinePlugin插件,导出上步获取的目录
5、web和service worker的通信
通过postMessage实现web和service worker间的通信
// 创建信道
var channel = new MessageChannel();
// port1留给自己
channel.port1.onmessage = e => {
console.log('main thread receive message...');
console.log(e);
}
console.log('给对方', window.RES_MAP);
// port2给对方
serviceWorker.postMessage(window.RES_MAP, [channel.port1]);
serviceWorker.addEventListener('statechange', function (e) {
// logState(e.target.state);
});
// sw
self.addEventListener('message', ev => {
console.log('sw receive message..');
console.log(ev);
fileMap = ev.data.RES_MAP;
var arr1 = [].slice.call(fileMap); // ['a', 'b', 'c']
// 取main thread传来的port2
ev.ports[0].postMessage('Hi, hello too');
});
小结
1、service worker让离线缓存成为可能,offline情况下也可以访问页面。然而销毁比较困难,更新会有问题。目前只能chrome://serviceworker-internals手动销毁,还待研究。
参考:
借助Service Worker和cacheStorage缓存及离线开发
Service Worker 简介
浅析Service Worker的更多相关文章
- [PWA] 9. Service worker registerion && service work's props, methods and listeners
In some rare cases, you need to ask user to refresh the browsser to update the version. Maybe becaus ...
- [PWA] 2. Service worker life cycle
Once serive worker is registered, the first time we go to the app, we cannot see the logs from servc ...
- [PWA] 1. Intro to Service worker
Service worker stays between our browser and noetwork requests. It can help to fetch data from cache ...
- Service Worker和HTTP缓存
很多人,包括我自己,初看Service Worker多一个Cache Storage的时候,就感觉跟HTTP长缓存没什么区别. 例如大家讲的最多的Service Worker能让网页离线使用,但熟悉H ...
- Service Worker
Service Worker 随着前端快速发展,应用的性能已经变得至关重要,关于这一点大佬做了很多统计.你可以去看看. 如何降低一个页面的网络请求成本从而缩短页面加载资源的时间并降低用户可感知的延时是 ...
- Service Worker基础知识整理
Service Worker是什么 service worker 是独立于当前页面的一段运行在浏览器后台进程里的脚本.它的特性将包括推送消息,背景后台同步, geofencing(地理围栏定位),拦截 ...
- Service Worker MDN英文笔记
前言: 以前学习基础知识的时候总看别人写的入门文章,但有时候还是一脸懵逼,直到自己用心阅读了MDN的英文文档才对基础知识的一些理论有了更深的理解,所以我在边阅读文档的时候边记录下帮助比较大的,也方便大 ...
- Service Worker 离线无法缓存Post请求的问题解决
许多非REST API甚至可以用于读取数据的POST请求:典型的例子是graphql.soap和其他rpcpapi.但是,Post请求不能在一个现成的渐进式Web应用程序中缓存和脱机使用.浏览器的缓存 ...
- JavaScript是如何工作的:Service Worker的生命周期及使用场景
摘要: 理解Service Worker. 原文:JavaScript 是如何工作的:Service Worker 的生命周期及使用场景 作者:前端小智 Fundebug经授权转载,版权归原作者所有. ...
随机推荐
- python标准库之shutil——可操作权限的文件操作库
转载自:https://www.jb51.net/article/145522.htm shutil模块提供了许多关于文件和文件集合的高级操作,特别提供了支持文件复制和删除的功能. 文件夹与文件操作 ...
- 【Python】【demo实验16】【练习实例】【打印所有水仙花数】
题目: 打印出所有的"水仙花数",所谓"水仙花数"是指一个三位数,其各位数字立方和等于该数本身.例如:153是一个"水仙花数",因为153= ...
- PHP中各种强大的函数
获取时间 data(https://www.cnblogs.com/mrluotong/p/5895375.html):strtotime() <?php echo "今天:&qu ...
- # Clion中编译多个cpp(实现单文件编译)
Clion中编译多个cpp(实现单文件编译) 在不做任何配置情况下,Clion工程下只能有一个main()函数,新建多个cpp会导致报main()函数重复定义的错误,所以默认情况下无法在一个工程下编译 ...
- # Python3微博爬虫[requests+pyquery+selenium+mongodb]
目录 Python3微博爬虫[requests+pyquery+selenium+mongodb] 主要技术 站点分析 程序流程图 编程实现 数据库选择 代理IP测试 模拟登录 获取用户详细信息 获取 ...
- 并不对劲的bzoj1972:loj2885:p2482[SDOI2010]猪国杀
题目大意 只能放链接了. 题目中有一点没说:并不是保证牌够用,而是在牌不够用时反复抽最后一张牌. 题解 发现玩家的数量比较少,所以可以不太在意时间够不够用. 考虑三件事:1.基本操作,如摸牌.出牌.玩 ...
- Java lesson17 homework
package lesson17; /**1. 创建自定义类Triangle包含如下属性: 最小夹角 a(整型.单位度) 最大夹角 b(整型.单位度) 编写构造方法包含两个参数(夹角),并根据参数计算 ...
- 如何用Mvc实现一个列表页面-异步加载
在接触Mvc后,开始有点觉得很累,什么都要自己做,完全没有WebForm的易用性: 大概用了一个月左右的时候,越用用顺手,就习惯了MVC的这种开发方式,灵活,简洁: 当初学习MVC,网上看资料,都是讲 ...
- 【ExtJs】在Ext.grid.Panel中,两列的值相乘作为第三列的值的实现
如: 商品总价=商品单价*商品数量 方法: 商品总价列,使用其renderer属性,为期定义一个方法,该方法将当前record中的另外两列中2个数据相乘后渲染到该商品总价列.
- vue打开到新页面,并传递参数
打开新页面,有两种方式, 一种是标签式: <router-link tag="a" target="_blank" :to="{path: '/ ...