浅析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处理字符串和列表元组的小技巧
变量值互换 a = 1 b = 100 # 变量值互换 a, b = b, a print('a:', a) print('b:', b) 输出结果: a: 100 b: 1 多个变量赋值 a, b, ...
- C语言Ⅰ博客作业09
这个作业属于那个课程 C语言程序设计II 这个作业要求在哪里 https://edu.cnblogs.com/campus/zswxy/CST2019-3/homework/10029 我在这个课程的 ...
- Wordpress 所有 hook 钩子
muplugins_loaded 在必须使用的插件加载之后. registered_taxonomy 对于类别,post_tag 等 Registered_post_type 用于帖子,页面等 plu ...
- 后缀数组 LCP--模板题
题意: 给你S串和T串,用T串的所有前缀去匹配S串(匹配值是最长公共子串). 问你总值相加是多少. 思路: 先把两个S,T串倒过来,再拼接 S#T 合成一串,跑一下后缀数组 在排序好的rank里计算每 ...
- paramiko-ssh-实现操作记录查看
在paramiko源码包中的demos目录下-> vim interactive.py
- [IOI2005]Riv河流
题目链接:洛谷,BZOJ 前置知识:莫得 题解 直接考虑dp.首先想法是设状态 \(dp[u][i]\) 表示u的子树内建 \(i\) 个伐木场且子树内木头都运到某个伐木场的最小花费.发现这样的状态是 ...
- Spring MVC(一)Spring MVC的原理
1.Spring MVC的目的 构建像Spring框架那样灵活和松耦合的Web应用程序. 2.Spring MVC中如何处理Request? 每当用户在Web浏览器中点击链接或者提交表单时,Reque ...
- 【sublime Text】sublime Text3安装可以使xml格式化的插件
应该有机会 ,会碰到需要格式化xml文件的情况. 例如,修改word转化的xml文件之后再将修改之后的xml文件转化为word文件. 但是,word另存的xml文件是没有格式的一片: 那怎么格式化 这 ...
- 深入理解计算机系统 第十二章 并发编程 part1 第二遍
三种构造并发程序的方法及其优缺点 1.进程 用这种方法,每个逻辑控制流都是一个进程,由内核来调度和维护.因为进程有独立的虚拟地址空间,想要和其他流通信,控制流必须使用某种显式的进程间通信机制. 优点: ...
- windowsformshost mouse event not transmit to it's parent control
in the case you can do it to fix: MouseEventArgs e = new MouseEventArgs(Mouse.PrimaryDevice, 0); e.R ...