主页 > 手机  > 

解锁浏览器内置API,助力跨标签/跨页面数据通信

解锁浏览器内置API,助力跨标签/跨页面数据通信
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.data console.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.index const imgUrl = avatarList[index] useBroadSendMsg(imgUrl) img.src = imgUrl // console.log(imgUrl) }) const broad = new BroadcastChannel('moment') broad.onmessage = function (e) { const { value } = e.data img.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.index const 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.data img.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) { // 获取发送消息的客户端 ID const 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.data if (type === 'change-avatar') { img.src = value } }) // DOM操作 ul.addEventListener('click', (e) => { // console.log(e.target.dataset.index) const li = e.target if (li.tagName === 'LI') { const index = li.dataset.index const 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.data if (type === 'change-avatar') { img.src = value } }) // 发送消息的按钮点击事件 // DOM操作 ul.addEventListener('click', (e) => { // console.log(e.target.dataset.index) const li = e.target if (li.tagName === 'LI') { const index = li.dataset.index const 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 = null const btn1 = document.querySelector('.btn1') const btn2 = document.querySelector('.btn2') btn1.onclick = () => { targetWindow = window.open('./other.html') } ul.addEventListener('click', (e) => { const li = e.target if (li.tagName === 'LI') { const index = li.dataset.index const 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.data img.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.target if (li.tagName === 'LI') { const index = li.dataset.index const imgUrl = avatarList[index] // 存储数据 localStorage.setItem('avatar', imgUrl) img.src = imgUrl } }) window.addEventListener('storage', (e) => { const newUrl = e.newValue img.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.target if (li.tagName === 'LI') { const index = li.dataset.index const imgUrl = avatarList[index] // 存储数据 localStorage.setItem('avatar', imgUrl) img.src = imgUrl } }) window.addEventListener('storage', (e) => { const newUrl = e.newValue img.src = newUrl }) </script> </html>

标签:

解锁浏览器内置API,助力跨标签/跨页面数据通信由讯客互联手机栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“解锁浏览器内置API,助力跨标签/跨页面数据通信

上一篇
C++之vector

下一篇