lxw124 / Interview

前端
2 stars 0 forks source link

从页面 A 打开一个新页面 B,B 页面关闭(包括意外崩溃),如何通知 A 页面? #65

Open lxw124 opened 4 years ago

lxw124 commented 4 years ago

正常关闭下可以在window.onunload或者window.beforeunload事件中使用window.opnener执行父页面的方法即可

//index1.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <a href="index2.html" target="blank">点击打开B页面</a>
    <script>

        function clicks(){
            console.log('页面2关闭了')
        }
    </script>
</body>
</html>
//index2.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <script>
        window.onbeforeunload=function(){
            window.opener.clicks()
        }
</script>
</body>
</html>

除了这个方法,还可以通过监听A页面的哈希路由,B页面在关闭前往A页面的路由传递参数即可

<html>
<head>
</head>
<body>
    <div>这是 A 页面</div>
    <button onclick="toB()">点击打开 B 页面</button>
    <script>
        window.name = 'A' // 设置页面名
        function toB() {
            window.open("B.html", "B") // 打开新页面并设置页面名
        }
        window.addEventListener('hashchange', function () {// 监听 hash
            alert(window.location.hash)
        }, false);
    </script>
</body>
</html>
<html>
<head>
</head>
<body>
    <div>这是 B 页面</div>
    <script>
        window.onbeforeunload = function (e) {
            window.open('A.html#close', "A") // url 挂参 跳回到已打开的 A 页面
            return '确定离开此页吗?';
        }
    </script>
</body>
</html>

除了这些还可以使用本地存储传递信息 监听页面异常关闭可以使用Serviceworker,在页面正常的时候每个10s往serviceworker脚本发送数据,超过15s没有更新数据说明页面崩溃了

//index1.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <!-- <button onclick="toB()">点击打开 B 页面</button> -->
    <a href="index2.html" target="blank">点击打开B页面</a>

</body>
</html>
//service-worker.js
const CHECK_CRASH_INTERVAL = 10 * 1000; // 每 10s 检查一次
const CRASH_THRESHOLD = 15 * 1000; // 15s 超过15s没有心跳则认为已经 crash
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
      console.log("页面发生崩溃")

      delete pages[id]

    }
  }
  if (Object.keys(pages).length == 0) {
    clearInterval(timer)
    timer = null
  }
}

this.addEventListener('message', (e) => {
  console.log("service worker 接收", e.data.type)
  const data = e.data;
  if (data.type === 'running') { // 正常心跳
    pages[data.id] = {
      t: Date.now()
    }
    if (!timer) {
      timer = setInterval(function () {
        checkCrash()
      }, CHECK_CRASH_INTERVAL)
    }
  } else if (data.type === 'clear') {
    delete pages[data.id]
  }
})
//index2.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div>这是 B crash 页面</div>
    <button onclick='handleCrash()'>点击我使页面崩溃</button>
    <script>
        function handleCrash(){
            var total="";  
            for (var i=0;i<1000000;i++)  
            {  
                var dom = document.createElement('span');
                dom.innerHTML="崩溃";
                document.getElementsByTagName("body")[0].appendChild(dom)
            }  
        }
        if ('serviceWorker' in navigator) {
            navigator.serviceWorker.register('service-worker.js', {
                scope: './'
            }).then(function (registration) {
                if (navigator.serviceWorker.controller !== null) {
                    let HEARTBEAT_INTERVAL = 5 * 1000; // 每五秒发一次心跳
                    let sessionId = "uuid()";
                    let heartbeat = function () {
                        console.log("页面发送 state:running") // running
                        navigator.serviceWorker.controller.postMessage({
                            type: 'running',
                            id: sessionId,
                            data: {} // 附加信息,如果页面 crash,上报的附加数据
                        });
                    }
                    window.addEventListener("beforeunload", function () {
                        console.log("页面发送 state:clear") // clear
                        navigator.serviceWorker.controller.postMessage({
                            type: 'clear',
                            id: sessionId
                        });
                    });
                    setInterval(heartbeat, HEARTBEAT_INTERVAL);
                    heartbeat();
                }
            }).catch(function (error) {
                // Something went wrong during registration. The service-worker.js file
                // might be unavailable or contain a syntax error.
            });
        } else {
            // The current browser doesn't support service workers.
        }
    </script>

</body>
</html>