当前位置: 首页 > news >正文

解锁浏览器内置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事件不会触发。

    三个常用字段:

    1. key:"键值对"
    2. newValue:"新值"
    3. 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>
    

    相关文章:

  1. 详解:事务注解 @Transactional
  2. 【后端开发面试题】每日 3 题(四)
  3. 【Python LeetCode 专题】面试经典 150 题
  4. 卷积运算是如何进行的?
  5. 详细对比所有开源许可及其不同版本
  6. 人工智能之数学基础:线性代数中的特殊矩阵
  7. 技术问题汇总:前端怎么往后端传一个日期?
  8. 【网络安全】从NA到P1,我是如何扩大思路的?
  9. CSS层叠上下文解析与应用
  10. 计算机毕业设计SpringBoot+Vue.js实验室管理系统(源码+文档+PPT+讲解)
  11. 学习笔记07——生产者-消费者模型4种实现方式
  12. LLM 大语言模型定义以及关键技术术语认知
  13. MySQL表字段数量上限解析
  14. 平衡二叉树
  15. web前端基础修炼手册
  16. Android Logcat 高效调试指南
  17. 【算法题解答·一】二分法
  18. JavaScript知识点5
  19. 新生校园报道小程序毕业系统设计
  20. 内容中台与企业内容管理架构解析
  21. 无锡做网站公司哪家比较好/推广平台排行榜
  22. 网站开发(源代码)/抖音企业推广
  23. 软件定制网站建设/上海seo公司排名
  24. 青海网站建设公司/哪里可以做
  25. 上海宝山网站制作/外贸google推广
  26. 三一重工的网站是哪家做的/浏阳廖主任打人