1 BrodcastChanner
概念
BroadcastChannel
接口表示给定源的任何浏览上下文都可以订阅的命名频道。它允许同源的不同浏览器窗口、标签页、frame 或者 iframe 下的不同文档之间相互通信。消息通过message事件进行广播,该事件在侦听该频道的所有BroadcastChannel
对象上触发,发送消息的对象除外。
何为源?
- 由用于访问它的URL的方案(协议)、主机名(域名)和端口定义。只有当协议、主机和端口都匹配时,两个对象才具有相同的源。
何为浏览上下文?
- 浏览器(browser)展示文档的环境。在现代浏览器中,通常是一个标签页(tab),也可能是一个窗体(window)或只是页面的一部分。
构成
- 构造函数:BroadcastChannel
- 实例属性:name,频道名称
- 实例方法:
- 发送消息:postMessage
- 关闭频道对象:close
- 事件:
- 收到消息时触发:message,也可以使用onmessage属性访问。
使用
1.创建一个链接到命名频道的对象
const broad = new BroadcastChannel('moment')
2.绑定事件,以便接收消息
broad.onmessage = function (e) {const { value } = e.dataconsole.log('value::',value)
}
3.按需进行消息发送
broad.postMessage({value: 'Hi,my baby'
})
代码落地
<!-- index.html -->
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>index</title><link rel="icon" type="image/png" href="../images/applogo.png" /><link rel="stylesheet" href="../css/common.css" /></head><body><div class="user-info"><img class="avatar" src="../images/2.jpg" alt="" /><p class="u-name">姓名:昔冰</p><p class="u- signature">个签:积善者必有余庆</p></div><ul class="avatar-list"><li class="avatar-item" data-index="0"></li><li class="avatar-item" data-index="1"></li><li class="avatar-item" data-index="2"></li></ul></body><script>const img = document.querySelector('.avatar')const avatarList = ['../images/1.jpg', '../images/2.jpg', '../images/3.png']const ul = document.querySelector('.avatar-list')ul.addEventListener('click', (e) => {// console.log(e.target.dataset.index)const index = e.target.dataset.indexconst imgUrl = avatarList[index]useBroadSendMsg(imgUrl)img.src = imgUrl// console.log(imgUrl)})const broad = new BroadcastChannel('moment')broad.onmessage = function (e) {const { value } = e.dataimg.src = value}const useBroadSendMsg = (data) => {setTimeout(() => {broad.postMessage({value: data})}, 500)}</script>
</html>
<!-- other.html -->
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>other</title><link rel="icon" type="image/png" href="../images/applogo.png" /><link rel="stylesheet" href="../css/common.css" /></head><body><div class="user-info"><img class="avatar" src="../images/2.jpg" alt="" /><p class="u-name">姓名:昔冰</p><p class="u- signature">个签:积善者必有余庆</p></div><ul class="avatar-list"><li class="avatar-item" data-index="0"></li><li class="avatar-item" data-index="1"></li><li class="avatar-item" data-index="2"></li></ul></body><script>const img = document.querySelector('.avatar')const avatarList = ['../images/1.jpg', '../images/2.jpg', '../images/3.png']const ul = document.querySelector('.avatar-list')ul.addEventListener('click', (e) => {// console.log(e.target.dataset.index)const index = e.target.dataset.indexconst imgUrl = avatarList[index]useBroadSendMsg(imgUrl)img.src = imgUrl// console.log(imgUrl)})const useBroadSendMsg = (data) => {setTimeout(() => {broad.postMessage({value: data})}, 500)}const broad = new BroadcastChannel('moment')broad.onmessage = function (e) {const { value } = e.dataimg.src = value}</script>
</html>
/* common.css */
* {padding: 0;margin: 0;box-sizing: border-box;
}ul {list-style: none;
}.avatar-list {margin: 20px auto;width: 300px;display: flex;justify-content: space-around;
}
.user-info {text-align: center;
}
.avatar {width: 100px;height: 100px;border-radius: 10px;
}.avatar-item {width: 80px;height: 80px;
}
.avatar-item:hover {cursor: pointer;
}
.avatar-item:nth-child(1) {background-image: url('../images/1.jpg');background-size: 100% 100%;
}.avatar-item:nth-child(2) {background-image: url('../images/2.jpg');background-size: 100% 100%;
}.avatar-item:nth-child(3) {background-image: url('../images/3.png');background-size: 100% 100%;
}
2 ServiceWorker
概念
ServiceWorker
接口提供了对 service worker 的引用。各个浏览上下文(例如页面、worker 等)可以与相同的 service worker 相关联,每个浏览上下文都可以通过唯一的ServiceWorker
对象访问。
Service worker 运行在 worker 上下文:因此它无法访问 DOM,相对于驱动应用的主 JavaScript 线程,它运行在其他线程中,所以不会造成阻塞。
何为worker上下文?
- 在浏览器环境中,Web Worker 是一种可以在浏览器后台运行脚本的机制,它允许在主线程之外创建独立的线程来执行 JavaScript 代码。Service Worker 就是基于 Web Worker 技术实现的,它运行在一个特殊的 worker 上下文环境中。
何为无法访问DOM?
- 由于 Service Worker 运行在独立的 worker 上下文中,与主页面的 DOM 环境是隔离的。
- 这意味着 Service Worker 不能直接对页面上的元素进行修改,比如改变文本内容、修改样式等。如果需要更新页面内容,Service Worker 可以通过向主页面发送消息,由主页面的 JavaScript 代码来完成 DOM 操作。
运行在其他线程中?
- 在浏览器中,主 JavaScript 线程负责处理用户交互、页面渲染和执行主页面的 JavaScript 代码。如果主线程执行了耗时的操作,如大量的计算或长时间的网络请求,会导致页面卡顿,用户体验变差。
- 而 Service Worker 运行在独立于主 JavaScript 线程的其他线程中,它有自己的执行栈和事件循环。这意味着 Service Worker 中的代码执行不会阻塞主页面的渲染和交互。
既然是独立出来的,那如何操作呢?
self
- 在 Service Worker 中,
self
是一个全局对象,它类似于主页面中的window
对象。self
代表当前的 Service Worker 实例本身,通过它可以访问 Service Worker 的各种方法和属性,也可以监听 Service Worker 生命周期中的各种事件。
self.clients
self.clients
是一个Clients
对象,它代表了当前与 Service Worker 关联的所有客户端(通常是浏览器的标签页或窗口)。通过self.clients
可以对这些客户端进行管理和操作,比如获取客户端列表、向客户端发送消息等。- 可以通过
client.postMessage()
方法向指定的客户端发送消息。
代码落地
// service-worker.js
// 监听消息事件
self.addEventListener('message', function (event) {// 获取发送消息的客户端 IDconst senderId = event.source.id// 获取所有受控的客户端self.clients.matchAll().then(function (clients) {clients.forEach(function (client) {// 避免将消息发送回发送者if (client.id !== senderId) {// 向其他客户端发送消息client.postMessage(event.data)}})})
})
<!-- index.html -->
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>index</title><link rel="icon" type="image/png" href="../images/applogo.png" /><link rel="stylesheet" href="../css/common.css" /></head><body><div class="user-info"><img class="avatar" src="../images/2.jpg" alt="" /><p class="u-name">姓名:昔冰</p><p class="u- signature">个签:积善者必有余庆</p></div><ul class="avatar-list"><li class="avatar-item" data-index="0"></li><li class="avatar-item" data-index="1"></li><li class="avatar-item" data-index="2"></li></ul></body><script>const img = document.querySelector('.avatar')const avatarList = ['../images/1.jpg', '../images/2.jpg', '../images/3.png']const ul = document.querySelector('.avatar-list')if ('serviceWorker' in navigator) {window.addEventListener('load', function () {navigator.serviceWorker.register('./service-worker.js').then(function (registration) {console.log('Service Worker registered successfully:', registration)// 监听来自Service Worker 的消息navigator.serviceWorker.addEventListener('message', function (event) {const { type, value } = event.dataif (type === 'change-avatar') {img.src = value}})// DOM操作ul.addEventListener('click', (e) => {// console.log(e.target.dataset.index)const li = e.targetif (li.tagName === 'LI') {const index = li.dataset.indexconst imgUrl = avatarList[index]// 发送消息navigator.serviceWorker.controller.postMessage({type: 'change-avatar',value: imgUrl})img.src = imgUrl}})}).catch(function (error) {console.error('Service Worker registration failed:', error)})})}</script>
</html>
<!-- other.html -->
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>other</title><link rel="icon" type="image/png" href="../images/applogo.png" /><link rel="stylesheet" href="../css/common.css" /></head><body><div class="user-info"><img class="avatar" src="../images/2.jpg" alt="" /><p class="u-name">姓名:昔冰</p><p class="u- signature">个签:积善者必有余庆</p></div><ul class="avatar-list"><li class="avatar-item" data-index="0"></li><li class="avatar-item" data-index="1"></li><li class="avatar-item" data-index="2"></li></ul></body><script>const img = document.querySelector('.avatar')const avatarList = ['../images/1.jpg', '../images/2.jpg', '../images/3.png']const ul = document.querySelector('.avatar-list')if ('serviceWorker' in navigator) {window.addEventListener('load', function () {navigator.serviceWorker.register('./service-worker.js').then(function (registration) {console.log('Service Worker registered successfully:', registration)// 监听来自 Service Worker 的消息navigator.serviceWorker.addEventListener('message', function (event) {const { type, value } = event.dataif (type === 'change-avatar') {img.src = value}})// 发送消息的按钮点击事件// DOM操作ul.addEventListener('click', (e) => {// console.log(e.target.dataset.index)const li = e.targetif (li.tagName === 'LI') {const index = li.dataset.indexconst imgUrl = avatarList[index]// 发送消息navigator.serviceWorker.controller.postMessage({type: 'change-avatar',value: imgUrl})img.src = imgUrl}})}).catch(function (error) {console.error('Service Worker registration failed:', error)})})}</script>
</html>
3 postMessage
概念
**window.postMessage()**方法可以安全地实现跨源通信。通常,对于两个不同页面的脚本,只有当执行它们的页面位于具有相同的协议(通常为 https),端口号(443 为 https 的默认值),以及主机 (两个页面的模数Document.domain设置为相同的值) 时,这两个脚本才能相互通信。
从广义上讲,一个窗口可以获得对另一个窗口的引用(比如
targetWindow = window.opener
),然后在窗口上调用targetWindow.postMessage()
方法分发一个MessageEvent消息。接收消息的窗口可以根据需要自由处理此事件。
怎么拿到另一个窗口的引用?
其他窗口的一个引用,比如 iframe 的 contentWindow 属性、执行window.open返回的窗口对象、或者是命名过或数值索引的window.frames。
代码落地
<!-- index.html -->
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>index</title><link rel="icon" type="image/png" href="../images/applogo.png" /><link rel="stylesheet" href="../css/common.css" /><link rel="stylesheet" href="./c-post.css" /></head><body><div class="user-info"><img class="avatar" src="../images/2.jpg" alt="" /><p class="u-name">姓名:昔冰</p><p class="u- signature">个签:积善者必有余庆</p></div><ul class="avatar-list"><li class="avatar-item" data-index="0"></li><li class="avatar-item" data-index="1"></li><li class="avatar-item" data-index="2"></li></ul><div class="edit-box"><button class="btn1">打开other页面</button></div></body><script>const img = document.querySelector('.avatar')const avatarList = ['../images/1.jpg', '../images/2.jpg', '../images/3.png']const ul = document.querySelector('.avatar-list')let targetWindow = nullconst btn1 = document.querySelector('.btn1')const btn2 = document.querySelector('.btn2')btn1.onclick = () => {targetWindow = window.open('./other.html')}ul.addEventListener('click', (e) => {const li = e.targetif (li.tagName === 'LI') {const index = li.dataset.indexconst imgUrl = avatarList[index]targetWindow.postMessage({ value: imgUrl }, 'http://127.0.0.1:5500')img.src = imgUrl}})</script>
</html>
<!-- other.html -->
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>other</title><link rel="icon" type="image/png" href="../images/applogo.png" /><link rel="stylesheet" href="../css/common.css" /><link rel="stylesheet" href="./c-post.css" /></head><body><div class="user-info"><img class="avatar" src="../images/2.jpg" alt="" /><p class="u-name">姓名:昔冰</p><p class="u- signature">个签:积善者必有余庆</p></div><ul class="avatar-list"><li class="avatar-item" data-index="0"></li><li class="avatar-item" data-index="1"></li><li class="avatar-item" data-index="2"></li></ul></body><script>const img = document.querySelector('.avatar')const avatarList = ['../images/1.jpg', '../images/2.jpg', '../images/3.png']const ul = document.querySelector('.avatar-list')const btn = document.querySelector('button')window.addEventListener('message', function (event) {if (event.origin == 'http://127.0.0.1:5500') {const { value } = event.dataimg.src = value}})</script>
</html>
4 Storage
当存储区域(localStorage 或 sessionStorage)被修改时,将触发 storage 事件。
对于全局localstorage,知道的是:
- 键值对形式
- 存储只能存储字符串内容,复杂类型数据需要使用JSON做序列化处理;
- 不会随页面刷新而丢失
使用
借助storage事件——在当前修改的这个页面不会触发,也就是行为发生的页面的storage事件不会触发。
三个常用字段:
- key:"键值对"
- newValue:"新值"
- oldValue:"旧值"
代码落地
<!-- index.html -->
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>index</title><link rel="icon" type="image/png" href="../images/applogo.png" /><link rel="stylesheet" href="../css/common.css" /></head><body><div class="user-info"><img class="avatar" src="../images/2.jpg" alt="" /><p class="u-name">姓名:昔冰</p><p class="u- signature">个签:积善者必有余庆</p></div><ul class="avatar-list"><li class="avatar-item" data-index="0"></li><li class="avatar-item" data-index="1"></li><li class="avatar-item" data-index="2"></li></ul></body><script>const img = document.querySelector('.avatar')const avatarList = ['../images/1.jpg', '../images/2.jpg', '../images/3.png']const ul = document.querySelector('.avatar-list')ul.addEventListener('click', (e) => {const li = e.targetif (li.tagName === 'LI') {const index = li.dataset.indexconst imgUrl = avatarList[index]// 存储数据localStorage.setItem('avatar', imgUrl)img.src = imgUrl}})window.addEventListener('storage', (e) => {const newUrl = e.newValueimg.src = newUrl})</script>
</html>
<!-- other.html -->
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>other</title><link rel="icon" type="image/png" href="../images/applogo.png" /><link rel="stylesheet" href="../css/common.css" /></head><body><div class="user-info"><img class="avatar" src="../images/2.jpg" alt="" /><p class="u-name">姓名:昔冰</p><p class="u- signature">个签:积善者必有余庆</p></div><ul class="avatar-list"><li class="avatar-item" data-index="0"></li><li class="avatar-item" data-index="1"></li><li class="avatar-item" data-index="2"></li></ul></body><script>const img = document.querySelector('.avatar')const avatarList = ['../images/1.jpg', '../images/2.jpg', '../images/3.png']const ul = document.querySelector('.avatar-list')ul.addEventListener('click', (e) => {const li = e.targetif (li.tagName === 'LI') {const index = li.dataset.indexconst imgUrl = avatarList[index]// 存储数据localStorage.setItem('avatar', imgUrl)img.src = imgUrl}})window.addEventListener('storage', (e) => {const newUrl = e.newValueimg.src = newUrl})</script>
</html>