Skip to content

页面通信

跨页面传值

postMessage

  • 获得窗口引用

  • 跨域

localStroage

js
// A.html
localStorage.setItem('message', 'hello')

// B.html
window.onstorage = evt => {
// evt.key, evt.oldValue, evt.newValue
}
// A.html
localStorage.setItem('message', 'hello')

// B.html
window.onstorage = evt => {
// evt.key, evt.oldValue, evt.newValue
}
  • 同源

BroadcastChannel

js
// A.html
const channel = new BroadcastChannel('tabs')
channel.onmessage = evt => {
// evt.data
}

// B.html
const channel = new BroadcastChannel('tabs')
channel.postMessage('hello')
// A.html
const channel = new BroadcastChannel('tabs')
channel.onmessage = evt => {
// evt.data
}

// B.html
const channel = new BroadcastChannel('tabs')
channel.postMessage('hello')
  • 不会持久化
  • 同源

SharedWorker

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

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

// 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))
   }
}
// A.html
var sharedworker = new SharedWorker('worker.js')
sharedworker.port.start()
sharedworker.port.onmessage = evt => {
// evt.data
}

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

// 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))
   }
}
  • 同源
  • 同源
  • 污染

Server

ajax

js
document.onvisibilitychange = () => {
  if (document.visibilityState === 'hidden') {
    // 切换走了
  } else if (document.visibilityState === 'visible') {
    // 切换来了
    // ajax获取数据。。。
  }
};
document.onvisibilitychange = () => {
  if (document.visibilityState === 'hidden') {
    // 切换走了
  } else if (document.visibilityState === 'visible') {
    // 切换来了
    // ajax获取数据。。。
  }
};

SSE/EventSource

EventSource 是服务器推送的一个网络事件接口。一个EventSource实例会对HTTP服务开启一个持久化的连接,以text/event-stream 格式发送事件, 会一直保持开启直到被要求关闭

前端

js
const es = new EventSource('http://localhost:3000/notification');

setTimeout(() => {
  es.close();
}, 5000);

es.onmessage = evt => {
  console.log('message', evt.data);
};
es.addEventListener('update', evt => {
  console.log('update', evt.data);
});
es.addEventListener(
  'close',
  () => {
    es.close();
  },
  false
);
const es = new EventSource('http://localhost:3000/notification');

setTimeout(() => {
  es.close();
}, 5000);

es.onmessage = evt => {
  console.log('message', evt.data);
};
es.addEventListener('update', evt => {
  console.log('update', evt.data);
});
es.addEventListener(
  'close',
  () => {
    es.close();
  },
  false
);

后端

js
const express = require('express');
const app = express();

// 缓存response
let resglobal;

app.get('/notification', (req, res) => {
  console.log('notification start');
  //必须为text/event-stream类型
  res.setHeader('Content-Type', 'text/event-stream');
  //   解决跨域问题
  res.setHeader('Access-Control-Allow-Origin', '*');
  resglobal = res;
  setInterval(() => {
    //   每1s派发message事件
    res.write(`event:message
data:${new Date().getMilliseconds()}

`);
  }, 1000);
  req.on('close', () => {
    //页面关闭、或者调用了EventSource的close方法
    //这个链接就会关闭,此处可以监听close方法
    console.log('close');
  });
});
app.get('/update', (req, res) => {
  // 调用/update时派发updater事件
  resglobal.write('event:update\ndata:update\n\n');
  res.status(200).end();
});

app.listen(3000);
const express = require('express');
const app = express();

// 缓存response
let resglobal;

app.get('/notification', (req, res) => {
  console.log('notification start');
  //必须为text/event-stream类型
  res.setHeader('Content-Type', 'text/event-stream');
  //   解决跨域问题
  res.setHeader('Access-Control-Allow-Origin', '*');
  resglobal = res;
  setInterval(() => {
    //   每1s派发message事件
    res.write(`event:message
data:${new Date().getMilliseconds()}

`);
  }, 1000);
  req.on('close', () => {
    //页面关闭、或者调用了EventSource的close方法
    //这个链接就会关闭,此处可以监听close方法
    console.log('close');
  });
});
app.get('/update', (req, res) => {
  // 调用/update时派发updater事件
  resglobal.write('event:update\ndata:update\n\n');
  res.status(200).end();
});

app.listen(3000);
  • 格式必须为:event:update\ndata:update\n\n
  • 服务端推送是单向的

Websocket

跨域

何为跨域

解决方法

cors

window.name

页面 a.html,b.html 同域

页面 c.html

c 传值给 a

c.html

js
window.name = '123'
window.name = '123'

b.html

可空白

a.html

html
<iframe
  id="iframe"
  src="http://localhost:30002/"
  onload="load()"
  frameborder="0"
></iframe>
<script>
  let first = true //第一次加载
  function load() {
    if (first) {
      let iframe = document.querySelector('#iframe')
      iframe.src = 'http://localhost:30001/b.html'
      first = false
    } else {
      console.log(iframe.contentWindow.name) //yuhua
    }
  }
</script>
<iframe
  id="iframe"
  src="http://localhost:30002/"
  onload="load()"
  frameborder="0"
></iframe>
<script>
  let first = true //第一次加载
  function load() {
    if (first) {
      let iframe = document.querySelector('#iframe')
      iframe.src = 'http://localhost:30001/b.html'
      first = false
    } else {
      console.log(iframe.contentWindow.name) //yuhua
    }
  }
</script>

postMessage

JSONP

window.domain

js
document.domain = 'a.com'
document.domain = 'a.com'

两个文档,只有在 document.domain 都被设定为同一个值,表明他们打算协作;或者都没有设定document.domain 属性并且 URL 的域是一致的,这两种条件下,一个文档才可以去访问另一个文档。

location.hash

页面 a.html,b.html 同域

页面 c.html

c 传值给 a

c.html

js
let iframe = document.createElement('iframe')
iframe.src = 'http://localhost:30001/b.html#18'
document.body.appendChild(iframe)
let iframe = document.createElement('iframe')
iframe.src = 'http://localhost:30001/b.html#18'
document.body.appendChild(iframe)

b.html

js
window.parent.parent.location.hash = location.hash
window.parent.parent.location.hash = location.hash

a.html

html
<iframe src="http://localhost:30002/" frameborder="0"></iframe>
<script>
  window.onhashchange = function() {
    console.log('location.hash', location.hash)
  }
</script>
<iframe src="http://localhost:30002/" frameborder="0"></iframe>
<script>
  window.onhashchange = function() {
    console.log('location.hash', location.hash)
  }
</script>