面试官也在看的前端面试资料

本题是 html 页面通信题,可以拆分成:

A 页面打开 B 页面,A、B 页面通信方式

据我所知,A、B 页面通信方式有:

url 传参

url 传参数没什么可说的





    
    
    A


    

A 页面


    
    
        window.name = 'A'
        function openB() {
            window.open("B.html", "B")
        }

        window.addEventListener('hashchange', function () {// 监听 hash
            alert(window.location.hash)
        }, false);
    


B:





    
    
    B
    


    

B 页面


    
    
        window.name = 'B'
        window.onbeforeunload = function (e) {
            window.open('A.html#close', "A")
            return '确定离开此页吗?';
        }
    


js点击打开新页面_js打开新页面_js button 打开新页面

A 页面通过 url 传递参数与 B 页面通信,同样通过监听 hashchange 事件,在页面 B 关闭时与 A 通信

postmessage

postMessage 是 h5 引入的 API,postMessage() 方法允许来自不同源的脚本采用异步方式进行有效的通信,可以实现跨文本文档、多窗口、跨域消息传递,可在多用于窗口间数据通信,这也使它成为跨域通信的一种有效的解决方案,简直不要太好用

A 页面打开 B 页面,B 页面向 A 页面发送消息:


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>A</title>
</head>
<body>
    <h1>A 页面</h1>
    <button type="button" onclick="openB()">B</button>
    <script>
        window.name = 'A'
        function openB({
            window.open("B.html?code=123""B")
        }
        window.addEventListener("message", receiveMessage, false);
        function receiveMessage(event{
            console.log('收到消息:', event.data)
        }
    
</script>
</body>
</html>


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>B</title>
    <button type="button" onclick="sendA()">发送A页面消息</button>
</head>
<body>
    <h1>B 页面</h1>
    <span></span>
    <script>
        window.name = 'B'
        function sendA({
            let targetWindow = window.opener
            targetWindow.postMessage('Hello A'"http://localhost:3000");
        }
    
</script>
</body>
</html>

js button 打开新页面_js点击打开新页面_js打开新页面

localStorage

// A
localStorage.setItem('testB''sisterAn');

// B
let testB = localStorage.getItem('testB');
console.log(testB)
// sisterAn

注意: localStorage仅允许你访问一个Document源(origin)的对象Storage;存储的数据将保存在浏览器会话中。如果 A 打开的 B 页面和 A 是不同源,则无法访问同一 Storage

WebSocket

基于服务端的页面通信方式,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种

SharedWorker

SharedWorker接口代表一种特定类型的 worker,可以从几个浏览上下文中访问,例如几个窗口、iframe 或其他 worker。它们实现一个不同于普通 worker 的接口,具有不同的全局作用域, SharedWorkerGlobalScope。

// A.html
var sharedworker = new SharedWorker('worker.js')
sharedworker.port.start()
sharedworker.port.onmessage = evt => {
 // evt.data
    console.log(evt.data) // hello A
}

// B.html
var sharedworker = new SharedWorker('worker.js')
sharedworker.port.start()
sharedworker.port.postMessage('hello A')

// worker.js
const ports = []
onconnect = e => {
const port = e.ports[0]
   ports.push(port)
   port.onmessage = evt => {
       ports.filter(v => v!== port) // 此处为了贴近其他方案的实现,剔除自己
       .forEach(p => p.postMessage(evt.data))
   }
}

Service Worker

js点击打开新页面_js打开新页面_js button 打开新页面

Service Worker 是一个可以长期运行在后台的 Worker,能够实现与页面的双向通信。多页面共享间的 Service Worker 可以共享,将 Service Worker 作为消息的处理中心(中央站)即可实现广播效果。

// 注册
navigator.serviceWorker.register('./sw.js').then(function ({
    console.log('Service Worker 注册成功');
})

// A
navigator.serviceWorker.addEventListener('message'function (e{
    console.log(e.data)
});

// B
navigator.serviceWorker.controller.postMessage('Hello A');

B 页面正常关闭,如何通知 A 页面

页面正常关闭时js打开新页面,会先执行 window.onbeforeunload ,然后执行 window.onunloadjs打开新页面,我们可以在这两个方法里向 A 页面通信

B 页面意外崩溃,又该如何通知 A 页面

页面正常关闭,我们有相关的 API,崩溃就不一样了,页面看不见了,JS 都不运行了,那还有什么办法可以获取B页面的崩溃?

全网搜索了一下,发现我们可以利用 window 对象的 load 和 beforeunload 事件,通过心跳监控来获取 B 页面的崩溃

 window.addEventListener('load'function ({
      sessionStorage.setItem('good_exit''pending');
      setInterval(function ({
         sessionStorage.setItem('time_before_crash'new Date().toString());
      }, 1000);
   });

   window.addEventListener('beforeunload'function ({
      sessionStorage.setItem('good_exit''true');
   });

   if(sessionStorage.getItem('good_exit') &&
      sessionStorage.getItem('good_exit') !== 'true') {
      /*
         insert crash logging code here
     */

      alert('Hey, welcome back from your crash, looks like you crashed on: ' + sessionStorage.getItem('time_before_crash'));
   }

使用 load 和 beforeunload 事件实现崩溃监控过程如下:

js打开新页面_js点击打开新页面_js button 打开新页面

图片来自:

这个方案巧妙的利用了页面崩溃无法触发 beforeunload 事件来实现的。

在页面加载时(load 事件)在 sessionStorage 记录 good_exit 状态为 pending,如果用户正常退出(beforeunload 事件)状态改为 true,如果 crash 了,状态依然为 pending,在用户第2次访问网页的时候(第2个load事件),查看 good_exit 的状态,如果仍然是 pending 就是可以断定上次访问网页崩溃了!

但有一个问题,本例中用 sessionStorage 保存状态,在用户关闭了B页面,sessionStorage 值就会丢失,所以换种方式,使用 Service Worker 来实现:

基于以上几点优势,完整设计一套流程如下:

代码如下:

js打开新页面_js点击打开新页面_js button 打开新页面

// B
if (navigator.serviceWorker.controller !== null) {
  let HEARTBEAT_INTERVAL = 5 * 1000 // 每五秒发一次心跳
  let sessionId = uuid() // B页面会话的唯一 id
  let heartbeat = function ({
    navigator.serviceWorker.controller.postMessage({
      type'heartbeat',
      id: sessionId,
      data: {} // 附加信息,如果页面 crash,上报的附加数据
    })
  }
  window.addEventListener("beforeunload"function({
    navigator.serviceWorker.controller.postMessage({
      type'unload',
      id: sessionId
    })
  })
  setInterval(heartbeat, HEARTBEAT_INTERVAL);
  heartbeat();
}

// 每 10s 检查一次,超过15s没有心跳则认为已经 crash
const CHECK_CRASH_INTERVAL = 10 * 1000 
const CRASH_THRESHOLD = 15 * 1000
const pages = {}
let timer
function checkCrash({
  const now = Date.now()
  for (var id in pages) {
    let page = pages[id]
    if ((now - page.t) > CRASH_THRESHOLD) {
      // 上报 crash
      delete pages[id]
    }
  }
  if (Object.keys(pages).length == 0) {
    clearInterval(timer)
    timer = null
  }
}

worker.addEventListener('message', (e) => {
  const data = e.data;
  if (data.type === 'heartbeat') {
    pages[data.id] = {
      tDate.now()
    }
    if (!timer) {
      timer = setInterval(function ({
        checkCrash()
      }, CHECK_CRASH_INTERVAL)
    }
  } else if (data.type === 'unload') {
    delete pages[data.id]
  }
})

参考:

来自:

最后

欢迎关注「三分钟学前端」,回复「交流」自动加入前端三分钟进阶群,每日一道编程算法题(第二天解答),助力你成为更优秀的前端开发!

js打开新页面_js button 打开新页面_js点击打开新页面

》》面试官也在看的前端面试资料《《

“在看和转发”就是最大的支持