跨页面通信方法总结

和前面跨域方法的应用场景不同,跨页面通信是为了两个不同的页面之间的通信问题,而前面的跨域方法主要解决不同源的客户端与服务端之间的通信问题。对于跨页面通信主要有以下这些方法:

doucument.domain

通过这种方式跨域的两个源需要满足一定的条件的,即两个源的域名需要是父子域的关系或者是相同的域。因为页面设置document.domain的值只能是当前域本身,或者是父域,而不能是其他不相关的域名。只有两个页面的document.domain都设置成相同的值,嵌入iframe的页面和iframe加载的页面才能相互获取到彼此的页面信息(包括DOM结构、window对象等)。

在实践中也发现需要注意的两个问题:

  1. 如果两个页面所在的源是一样的,可以访问到嵌入的window对象上的全局变量,但是如果两个页面所在的域名相同但端口不同或者是其他情况,则无法访问。
  2. 需要在嵌入的同源iframe加载完成之后获取子页面window对象上的数据,否则拿到的值还是undefined。

window.name

浏览器具有这样一个特性:同一个标签页或者同一个iframe框架加载过的页面共享相同的window.name属性值,意味着只要是在同一个标签页里面打开过的页面(不管是否同源),这些页面上window.name属性值都是相同的。利用这个特性,就可以将这个属性作为在不同页面之间传递数据的介质。

如果是通过iframe+window.name这种方式在完全没有父子域关系的两个源之间传递数据(假设源A要获取源B中的数据),源A页面上的iframe在加载源B的目标页面(源B页面把数据设置在window.name属性上)之后还需要再跳转到源A的某个页面上,以便于嵌入iframe的页面通过(上面介绍的)和在iframe中的页面将document.domain都设置为源A的方式来获取iframe中的数据。示例代码如下:

HTML5 cross-document message

HTML5中引入了另外一种跨页面通信的方式,称为跨文档消息传送。同样可以实现主页面和嵌入的iframe子页面(或者由当前页面打开的页面)之间完成数据的传递,另外这种方法也可以用于当前JavaScript引擎线程和其他worker线程之间完成数据交换。如果是与通过iframe加载的子页面进行通信,则需要先获取到接收数据的目标页面的window对象(具体通过前面提到的设置相同的document.domain方法来获取),通过该对象的postMessage方法可以向目标页面发送数据。

如果是需要和页面上的worker进行通信,直接调用创建出来的Worker实例的postMessage方法,在Worker实例执行的脚本中则通过self或者this来访问Worker实例,进而调用postMessage方法来完成通信。需要注意的是,构造Worker实例时传入的脚本必须和当前页面是同源

BroadcastChannel

这是一种可以实现在同源的多个页面之间广播消息的机制。

localStorage

localStorage是HTML5引入的客户端存储方案,通过localStorage存储的内容会一直保存在客户端,除非调用removeItem方法显式移除,否则内容将永久保留。MDN上对localStorage的介绍也提到了一种通过cookie在不支持localStorage的浏览器上实现localStorage的方法,通过将cookie的过期时间设置为未来很长之后的一个时间点可以模拟localStorage永久保留的特性,而在模拟localStorage移除存储内容时则将对应的cookie删除。更进一步,如果不设置cookie的过期时间,还可以用来模拟浏览器中的另一种客户端存储方案--sessionStorage。和cookie不同的是,localStorage提供的存储容量上限更大。

避免LocalStorage存满的问题:

  1. 划分子域,各域名下存储空间由各业务小组管理,互不干扰;
  2. 跨页面传递数据,不使用客户端存储方案

前面也提到了,localStorage存储的内容是以源为单位进行管理的,这意味着即使域名相同,端口不同的页面也无法通过localStorage进行通信的。在浏览器的多个标签页中分别打开多个同源页面,当其中某个页面在localStorage中添加、修改或删除某些字段或清空存储的内容时,会触发其他页面中的window对象监听的storage事件(当前修改localStorage的页面不触发),通过这种方式也可以实现跨页面通信。

sessionStorage可否跨标签页访问?答案是不能,每个标签页有独立的会话,sessionStorage中存储的数据不共享。另外,sessionStorage在页面刷新之后依然存在。