Skip to content

Commit f3674b9

Browse files
committed
docs: 新增HTML5连接下篇
1 parent 5c8ddd1 commit f3674b9

File tree

3 files changed

+390
-319
lines changed

3 files changed

+390
-319
lines changed

.vitepress/data.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ const articles = [
5050
'HTML5系列之存储篇上',
5151
'HTML5系列之存储篇下',
5252
'HTML5系列之连接篇上',
53+
'HTML5系列之连接篇下',
5354
'HTML5系列之性能篇',
5455
'SVG入门',
5556
'MathML入门'

src/html/HTML5系列之连接篇上.md

Lines changed: 1 addition & 319 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ tags:
44
- HTML5
55
---
66

7-
关于"连接"部分的学习分为两篇文章介绍,这是上篇,介绍Ajax、Comet、postMessage、Worker和WebSockets
7+
关于"连接"部分的学习分为两篇文章介绍,这是上篇,介绍Ajax、Comet和WebSocket
88

99
## Ajax
1010
### 什么是Ajax
@@ -318,324 +318,6 @@ Web服务器发起通信并异步发送消息到客户端,某种意义上,Aj
318318

319319
> 基于Ajax和Comet可以构建更高级的通信协议,比如RPC(远程过程调用)、发布订阅事件系统。
320320
321-
## 跨域消息传递——postMessage
322-
### 适用范围
323-
允许脚本显式打开的一个新窗口(window.open)或者嵌套其中的窗体(iframe)与当前窗口进行通信
324-
325-
### postMessage参数说明
326-
- 第一个参数表示要传递的消息
327-
- 第二个参数表示目标窗口的源,可以传递一个URL,仅协议、主机和端口号有效,其余部分会被忽略。若同源,传`/`,若无限制,传`*`
328-
329-
### 示例
330-
- [Demo](https://github.com/muzhidong/blog-demo/tree/main/docs/01html/%E8%BF%9E%E6%8E%A5/postMessage)
331-
332-
## Web Worker
333-
### 背景
334-
设计成单线程的理论是,JS必须不能运行太长时间,否则会出现卡顿,浏览器无法对用户输入作出响应,而Web Worker弥补浏览器无法多线程的缺陷
335-
336-
### 概念
337-
创建新的运行时,有自己的栈、堆、队列,不影响页面的渲染
338-
339-
### 特点
340-
- 处理耗时操作
341-
- 同源限制
342-
- 无法访问window和document,不能操作DOM
343-
- 无法使用文件系统API
344-
- 与主线程不共用同一个上下文环境,即存在线程间数据共享、同步、通信等问题,在ES8提出了[SharedArrayBuffer和Atomics](/javascript/ES6+新特性你知道多少#es2017),实现线程间资源共享,解决线程间同步或通信问题。
345-
346-
### Worker API
347-
- Worker
348-
349-
事件属性
350-
- onmessage
351-
- onerror
352-
- onmessageerror
353-
```javascript
354-
// main.js
355-
const worker = new Worker('./worker.js', {
356-
// 设置worker名称
357-
name: 'test'
358-
})
359-
worker.onmessageerror = function(e) {}
360-
361-
// worker.js
362-
console.log(self.name)
363-
self.onmessageerror = function(e) {}
364-
```
365-
366-
方法
367-
- postMessage
368-
```javascript
369-
// 转移数据
370-
// 拷贝方式发送二进制数据,会造成性能问题,因为默认情况下浏览器会生成一个原文件的拷贝。为了解决这个问题,JavaScript允许主线程把二进制数据直接转移给子线程,但是一旦转移,主线程就无法再使用这些二进制数据了,这是为了防止出现多个线程同时修改数据的麻烦局面。这种转移数据的方法叫做Transferable Objects,使用方式如下:worker.postMessage(arrayBuffer, [arrayBuffer]);
371-
const ab = new ArrayBuffer(1);
372-
worker.postMessage(ab, [ab]);
373-
```
374-
- terminate
375-
```javascript
376-
// 主线程关闭Worker
377-
worker.terminate();
378-
```
379-
380-
- WorkerGlobalScope
381-
- 除了windowdocument对象外,其他API基本可以使用
382-
- close:自行关闭worker
383-
```javascript
384-
// Worker线程自行关闭Worker
385-
self.close();
386-
```
387-
- importScripts:同步加载多个脚本,当中有一个脚本加载出错,则剩余脚本不再载入和运行
388-
```javascript
389-
// Worker线程加载脚本
390-
importScripts('script1.js', 'script2.js');
391-
```
392-
393-
> Worker执行模型:worker从上到下同步运行代码,然后进入一个异步阶段。当有监听消息,worker永远不会自动退出;而若没有监听消息,则直到所有任务相关的回调函数都被调用,且再也没有挂起的任务时,worker会自动退出
394-
395-
### Worker子类
396-
- SharedWorker
397-
```html
398-
<h3>共享线程SharedWorker</h3>
399-
<button id="likeBtn">点赞</button>
400-
<p>收获了<span id="likedCount">0</span>个👍</p>
401-
<script id="shared-worker" type="app/worker">
402-
console.log("shared-worker");
403-
let like = 0;
404-
onconnect = function (e) {
405-
const port = e.ports[0];
406-
port.onmessage = function () {
407-
port.postMessage(++like);
408-
};
409-
};
410-
</script>
411-
<script>
412-
const likeBtn = document.querySelector("#likeBtn");
413-
const likedCountEl = document.querySelector("#likedCount");
414-
415-
const blob = new Blob([document.querySelector('#shared-worker').textContent]);
416-
const url = window.URL.createObjectURL(blob);
417-
const worker = new SharedWorker(url);
418-
419-
worker.port.start();
420-
likeBtn.addEventListener("click", function () {
421-
worker.port.postMessage("like");
422-
});
423-
worker.port.onmessage = function (e) {
424-
likedCountEl.innerHTML = e.data;
425-
};
426-
</script>
427-
```
428-
429-
- ServiceWorker
430-
431-
用途
432-
- 离线资源缓存与更新
433-
- 后台消息传递
434-
- 网络代理
435-
- 消息推送
436-
437-
使用注意事项
438-
- 不要给service-worker.js文件带版本号,防止文件变更,读取缓存
439-
- 不要给service-worker.js文件资源设置缓存
440-
441-
示例
442-
```javascript
443-
// 询问用户刷新
444-
// 思路:
445-
// 1、浏览器检测到存在新的SW时,安装并让它等待,同时触发updatefound事件(浏览器执行,无需编码)
446-
// 2、监听updatefound事件,弹出一个提示条,询问用户是否更新SW
447-
// 3、若用户确认,则向处在等待的SW发送消息,要求其执行skipWaiting并取得控制权
448-
// 4、SW的变化触发controllerchange事件,在该事件的回调中刷新页面
449-
450-
// index.js
451-
function emitUpdate() {
452-
var event = document.createEvent('Event');
453-
event.initEvent('sw.update', true, true);
454-
window.dispatchEvent(event);
455-
}
456-
457-
if ('serviceWorker' in navigator) {
458-
navigator.serviceWorker.register('/service-worker.js').then(function (reg) {
459-
if (reg.waiting) {
460-
emitUpdate();
461-
return;
462-
}
463-
// 监听updatefound事件
464-
reg.onupdatefound = function () {
465-
var installingWorker = reg.installing;
466-
installingWorker.onstatechange = function () {
467-
switch (installingWorker.state) {
468-
// 弹出一个提示条
469-
case 'installed':
470-
if (navigator.serviceWorker.controller) {
471-
emitUpdate();
472-
}
473-
break;
474-
}
475-
};
476-
};
477-
}).catch(function(e) {
478-
console.error('Error during service worker registration:', e);
479-
});
480-
481-
// 监听controllerchange事件
482-
let refreshing = false
483-
navigator.serviceWorker.addEventListener('controllerchange', () => {
484-
// 避免使用Chrome Dev Tools的Update on Reload功能时引发无限刷新
485-
if (refreshing) {
486-
return
487-
}
488-
refreshing = true;
489-
window.location.reload();
490-
})
491-
}
492-
493-
window.addEventListener('sw.update', function(){
494-
// 弹框
495-
})
496-
497-
// 用户确认按钮点击事件
498-
confirmEl.addEventListener('click', function(){
499-
try {
500-
navigator.serviceWorker.getRegistration().then(reg => {
501-
reg.waiting.postMessage('skipWaiting');
502-
});
503-
} catch (e) {
504-
window.location.reload();
505-
}
506-
})
507-
508-
509-
// service-worker.js
510-
// 接收消息,更新sw
511-
self.addEventListener('message', event => {
512-
if (event.data === 'skipWaiting') {
513-
self.skipWaiting();
514-
}
515-
})
516-
```
517-
518-
```javascript
519-
// 监控页面崩溃
520-
// index.js
521-
if(navigator.serviceWorker.controller !== null) {
522-
// 心跳间隔
523-
const HEADBEAT_INTERVAL = 5000;
524-
const sessionId = uuid()
525-
const heartbeat = () => {
526-
navigator.serviceWorker.controller.postMessage({
527-
type: 'heartbeat',
528-
id: sessionId,
529-
data: {
530-
// 添加附加数据
531-
}
532-
})
533-
}
534-
// 心跳检测
535-
setInterval(heartbeat, HEADBEAT_INTERVAL)
536-
heartbeat()
537-
538-
// 通知sw删除当前监控记录
539-
window.addEventListener('beforeunload', () => {
540-
navigator.serviceWorker.controller.postMessage({
541-
type: 'unload',
542-
id: sessionId
543-
})
544-
})
545-
}
546-
547-
// worker.js
548-
// 检查崩溃间隔
549-
const CHECK_CRASH_INTERVAL = 10000;
550-
// 崩溃阈值
551-
const CRASH_THRESHOLD = 15000;
552-
const pages = {}
553-
let timer
554-
const checkCrash = () => {
555-
const now = Date.now()
556-
for(let id in pages) {
557-
const page = pages[id]
558-
if(now - page.t > CRASH_THRESHOLD) {
559-
// 上报crash
560-
// 删除监控记录
561-
delete pages[id]
562-
}
563-
}
564-
if(Object.keys(pages).length === 0) {
565-
clearInterval(timer)
566-
timer = null
567-
}
568-
}
569-
self.addEventListener('message', event => {
570-
const data = event.data
571-
if(data.type === 'heartbeat') {
572-
pages[data.id] = {
573-
t: Date.now(),
574-
}
575-
if(!timer) {
576-
timer = setInterval(checkCrash, CHECK_CRASH_INTERVAL)
577-
}
578-
} else if(data.type === 'unload') {
579-
delete pages[data.id]
580-
}
581-
})
582-
```
583-
584-
```javascript
585-
// 加速边缘计算
586-
self.addEventListener('fetch', event => {
587-
event.respondWith(handle(event.request))
588-
})
589-
async function handle(request) {
590-
const url = new URL(request.url)
591-
if (url.pathname == "/") {
592-
// 这是一个首页请求,重定向到特定国家的路径,如给美国用户发送“/US/”
593-
const country = request.headers.get("CF-IpCountry")
594-
url.pathname = "/" + country + "/"
595-
return Response.redirect(url, 302)
596-
} else if (url.pathname.startsWith("/images/")) {
597-
// 这是一个图片请求,阻止第三方访问者盗链  
598-
const referrer = request.headers.get("Referer")
599-
if (referrer && new URL(referrer).hostname != url.hostname) {
600-
return new Response("Hot linking not allowed.", {
601-
status: 403
602-
})
603-
}    
604-
// 盗链检查通过,直接从谷歌云存储提供图片服务节省服务成本
605-
// 根据Cache-Control头信息,图片会在Cloudflare的边缘服务器缓存
606-
url.hostname = "example-bucket.storage.googleapis.com"    
607-
return fetch(url, request)  
608-
} else {    
609-
// 定期请求,转发给源服务器  
610-
return fetch(request)  
611-
}
612-
}
613-
```
614-
615-
### 示例
616-
- worker代码和主线程代码在同一页面
617-
```html
618-
<!-- worker脚本 -->
619-
<!-- 注意script标签需指定id属性,且type属性是一个浏览器不认识的值 -->
620-
<script id="worker" type="app/worker">
621-
addEventListener('message', function (e) {
622-
postMessage("I'm fine.");
623-
}, false);
624-
</script>
625-
<!-- 主线程脚本 -->
626-
<script>
627-
// Blob内容是子线程代码
628-
var blob = new Blob([document.querySelector('#worker').textContent]);
629-
var url = window.URL.createObjectURL(blob);
630-
var worker = new Worker(url);
631-
worker.postMessage('How are you?')
632-
worker.onmessage = function (e) {
633-
console.log(e.data);
634-
};
635-
</script>
636-
```
637-
638-
- [Worker and ServiceWorker Demo](https://github.com/muzhidong/blog-demo/tree/main/docs/01html/%E8%BF%9E%E6%8E%A5/Worker)
639321

640322
## WebSocket
641323
### 概念

0 commit comments

Comments
 (0)