Chome版本
- 金丝雀
- 开发版
- 测试版
- 正式版
Web Worker
Web Worker 是HTML5标准的一部分,这一规范定义了一套 API,它允许一段JavaScript程序运行在主线程之外的另外一个线程中。当然 Web Worker 提供的多线程编程能力并不像我们传统意义上的多线程编程,Worker 线程之间,不会共享任何作用域或资源,他们间唯一的通信方式就是一个基于事件监听机制的 message;
Web Worker 规范中定义了两类工作线程,分别是**专用线程Dedicated Worker
和共享线程 Shared Worker
**
**专用线程Dedicated Worker
**只能为一个页面所使用
**共享线程Shared Worker
**则可以被多个页面所共享。
[浅谈Web Worker] juejin.cn/post/684490…
只需调用Worker() 构造函数并传入一个要在 worker 线程内运行的脚本的URI,即可创建一个新的worker。
var myWorker = new Worker("my_task.js");
// my_task.js中的代码
var i = 0;
function timedCount(){
i = i+1;
postMessage(i);
setTimeout(timedCount, 1000);
}
timedCount();
另外,通过URL.createObjectURL()创建URL对象,可以实现创建内嵌的worker
var myTask = `
var i = 0;
function timedCount(){
i = i+1;
postMessage(i);
setTimeout(timedCount, 1000);
}
timedCount();
`;
var blob = new Blob([myTask]);
var myWorker = new Worker(window.URL.createObjectURL(blob));
注意:传入 Worker 构造函数的参数 URI 必须遵循同源策略。如果 此URL有一个无效的语句,或者违反同源策略,一个
SECURITY_ERR
类型的DOMException
被抛出。
Worker线程数据通讯方式
Worker 与其主页面之间的通信是通过 onmessage 事件和 postMessage() 方法实现的。
在主页面与 Worker 之间传递的数据是通过拷贝,而不是共享来完成的。传递给 Worker 的对象需要经过序列化,接下来在另一端还需要反序列化。页面与 Worker 不会共享同一个实例,最终的结果就是在每次通信结束时生成了数据的一个副本。
也就是说,Worker 与其主页面之间只能单纯的传递数据,不能传递复杂的引用类型:如通过构造函数创建的对象等。并且,传递的数据也是经过拷贝生成的一个副本,在一端对数据进行修改不会影响另一端。
var myTask = `
onmessage = function (e) {
var data = e.data;
data.push('hello');
console.log('worker:', data); // worker: [1, 2, 3, "hello"]
postMessage(data);
};
`;
var blob = new Blob([myTask]);
var myWorker = new Worker(window.URL.createObjectURL(blob));
myWorker.onmessage = function (e) {
var data = e.data;
console.log('page:', data); // page: [1, 2, 3, "hello"]
console.log('arr:', arr); // arr: [1, 2, 3]
};
var arr = [1,2,3];
myWorker.postMessage(arr);
应用场景
Web Worker 的实现为前端程序带来了后台计算的能力,可以实现主 UI 线程与复杂计运算线程的分离,从而极大减轻了因计算量大而造成 UI 阻塞而出现的界面渲染卡、掉帧的情况,并且更大程度地利用了终端硬件的性能;
同时把程序之间的任务更清晰、条理化;
其主要应用有几个场景:
- 对于图像、视频、音频的解析处理;
- canvas 中的图像计算处理;
- 大量的 ajax 请求或者网络服务轮询;
- 大量数据的计算处理(排序、检索、过滤、分析...);
Service Worker / PWA
借助Service Worker和cacheStorage缓存及离线开发
HTML5离线开发技术已经出现很多年了,但并不看好,用一句话解释就是“投入产出比有些低”。
对于web应用,掉线不能使用是理所当然的,绝不会有哪个开发人员会因为网页在没网的时候打不开被测试MM提bug,或者被用户投诉,所以,我们的web页面不支持离线完全不会有什么影响。但如果我们希望支持离线,那投入的精力和成本并不小,html5 manifest缓存技术还需要服务端配合,跨团队协作让工作成本就上去了。
Service Worker概念
平常浏览器窗口中跑的页面运行的是主JavaScript线程,DOM和window全局变量都是可以访问的。而Service Worker是走的另外的线程,可以理解为在浏览器背后默默运行的一个线程,脱离浏览器窗体。因此,window以及DOM都是不能访问的,此时我们可以使用self
访问全局上下文。
Workers开辟的新线程是没有“窗体”这个概念的,都是在浏览器背后悄悄运行的线程,没有窗体的概念也就意味着没有
window
对象,因此Service Workers和Web Workers相关的脚本中是不能使用window
这个对象的,在non-window上下文的环境中,我们可以使用self
来表示全局作用域。注意,只能是self
,例如
self.addEventListener('install', function(event) { // ... });
限制
- 由于Service Worker设计为完全异步,同步API(如
XHR
和localStorage
)不能在Service Worker中使用。 - Service Worker必须是
https
协议的
Service Worker的生命周期
Service Worker 生命周期如下:
Service Worker是有对应的事件名进行捕获的,为:
self.addEventListener('install', function(event) { /* 安装后... */ });
self.addEventListener('activate', function(event) { /* 激活后... */ });
最后,Service Worker还支持fetch
事件,来响应和拦截各种请求。
self.addEventListener('fetch', function(event) { /* 请求后... */ });
Service Worker的兼容性
桌面端Chrome和Firefox可用,IE不可用。移动端Chrome可用,以后估计会快速支持。
借助Service Worker和cacheStorage离线开发的固定套路
-
页面上注册一个Service Worker,例如:
if ('serviceWorker' in navigator) { navigator.serviceWorker.register('./sw-demo-cache.js'); }
-
sw-demo-cache.js
这个JS中复制如下代码:
var VERSION = 'v1'; // 缓存 self.addEventListener('install', function(event) { event.waitUntil( caches.open(VERSION).then(function(cache) { return cache.addAll([ './start.html', './static/jquery.min.js', './static/mm1.jpg' ]); }) ); }); // 缓存更新 self.addEventListener('activate', function(event) { event.waitUntil( caches.keys().then(function(cacheNames) { return Promise.all( cacheNames.map(function(cacheName) { // 如果当前版本和缓存版本不一致 if (cacheName !== VERSION) { return caches.delete(cacheName); } }) ); }) ); }); // 捕获请求并返回缓存数据 self.addEventListener('fetch', function(event) { event.respondWith(caches.match(event.request).catch(function() { return fetch(event.request); }).then(function(response) { caches.open(VERSION).then(function(cache) { cache.put(event.request, response); }); return response.clone(); }).catch(function() { return caches.match('./static/mm1.jpg'); })); });
-
把
cache.addAll()
方法中缓存文件数组换成你希望缓存的文件数组。
预览:借助Service Worker和cacheStorage缓存及离线开发demo
install
调用了caches.open()
和我们想要的缓存名称, 之后调用 cache.addAll()
并传入文件数组。 这是一个promise 链( caches.open() 和 cache.addAll() )。 event.waitUntil()
方法接受一个承诺,并使用它来知道安装需要多长时间,以及它是否成功。
activate
在激活回调中发生的一个常见任务是缓存管理。你要在激活回调中这样做的原因是,如果你要在安装步骤中清除所有旧的缓存,任何保留所有当前页面的旧 Service Worker 将会突然停止服务来自该缓存的文件。
这里提供了一个如何从缓存中删除一些不在白名单中的文件的例子(在本例中,有VERSION=v1 实体):
fetch
在 event.respondWith()
中,我们传递了一个来自 caches.match()
的 promise。 此方法查看请求,并查找来自 Service Worker 创建的任何缓存的任何缓存结果。
设计模式
抽象工厂模式
// 底层
function artFactory() {
function getFunction(path, params) {
console.log(path, params);
}
function postFunction(path, params) {
console.log(path, params);
}
function putFunction(path, params) {
console.log(path, params);
}
return function (type, path, params) {
switch(type) {
case 'get':
this.getFunction(path, params)
break;
case 'post':
this.postFunction(path, params)
break;
default:
this.putFunction(path, params)
break;
}
}
}
// 业务层
function user() {
const USER_URL_MAP = {
getUser: 'getUserUrl',
updateUser: 'updateUserUrl',
}
return {
getUser(params) {
return artFactory()('get', USER_URL_MAP['getUser'], params)
},
postUpdate(params) {
return artFactory()("post", USER_URL_MAP['updateUser'], params)
}
}
}
function sendUrl(businessType) {
switch(businessType) {
case 'user':
return user;
default:
console.log("无");
}
}
const userAction = sendUrl('user')
userAction.getUser("usrname")
观察者模式 (响应式数据原理)
tree shaking
Tree shaking 是一种通过清除多余代码方式来优化项目打包体积的技术
原理是通过ES6 module
引入了静态分析,可以在编译的时候判断哪些模块代码是真正加载被加载,判断哪些是未使用的,未被引用的进而删除那些没有被使用到的模块。
common.js 和 es6 中模块引入的区别
CommonJS 是一种模块规范,最初被应用于 Nodejs,成为 Nodejs 的模块规范。运行在浏览器端的 JavaScript 由于也缺少类似的规范,在 ES6 出来之前,前端也实现了一套相同的模块规范 (例如: AMD),用来对前端模块进行管理。自 ES6 起,引入了一套新的 ES6 Module 规范,在语言标准的层面上实现了模块功能,而且实现得相当简单,有望成为浏览器和服务器通用的模块解决方案。但目前浏览器对 ES6 Module 兼容还不太好,我们平时在 Webpack 中使用的 export 和 import,会经过 Babel 转换为 CommonJS 规范。在使用上的差别主要有:
1、CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
2、CommonJS 模块是运行时加载,ES6 模块是编译时输出接口(静态编译)。
3、CommonJs 是单个值导出,ES6 Module 可以导出多个
4、CommonJs 是动态语法可以写在判断里,ES6 Module 静态语法只能写在顶层
5、CommonJs 的 this 是当前模块,ES6 Module 的 this 是 undefined