Skip to content

基于postMessage+BroadcastChannel+localStorage+互信域名的前端页面数据通信解决方案

License

Notifications You must be signed in to change notification settings

xxxily/broadcast-message

Repository files navigation

broadcast-message

broadcast-message

基于postMessage+BroadcastChannel+localStorage+互信域名的前端页面数据通信解决方案

特性

  • 继承了BroadcastChannel通信的优点:支持跨TAB通信
  • 继承了localStorage通信的优点:兼容性好
  • 继承了postMessage通信的优点:支持跨域通信
  • 一套脚本解决前端数据通信面临的众多情况
  • 支持同页、弹窗页、父子嵌套页、跨域、跨TAB页的通信

安装

# npm
npm install broadcast-message

# yarn
yarn add broadcast-message

中国大陆用户可使用阿里源进行加速安装

# npm
npm install broadcast-message --registry=https://registry.npmmirror.com

# yarn
yarn add broadcast-message --registry=https://registry.npmmirror.com

使用

同域下基本使用示例

import BroadcastMessage from 'broadcast-message'

const bmOpts = { channelId: 'demo', allowLocalBroadcast: true }
const broadcastMessage = new BroadcastMessage(bmOpts)

/* 注册接收消息事件 */
broadcastMessage.addEventListener("message", event => {
  console.log(`[BroadcastMessage-Event]`, event.data)
})

/* 发送消息 */
broadcastMessage.postMessage(`[demo] ${Date.now()}`)

指定使用localStorage来中转消息

import BroadcastMessage from 'broadcast-message'

const bmOpts = { channelId: 'localStorage-demo', allowLocalBroadcast: true, transportType: 'localStorage' }
const broadcastMessage = new BroadcastMessage(bmOpts)

/* 注册接收消息事件 */
broadcastMessage.addEventListener("message", function (event) {
  console.log(`[BroadcastMessage-Event]`, event.data)
})

/* 发送消息 */
broadcastMessage.postMessage(`[localStorage-demo] ${Date.now()}`)

PS: 一般不建议手动指定transportType类型,broadcast-message默认会根据浏览器的支持情况自动选择BroadcastChannel还是localStorage,另外你应该了解的是:BroadcastChannel的通信效率比localStorage高

跨域下基本使用示例

假设:

  • 域名A是消息接收方
  • 域名B是消息发送方

域名A的代码示例:

import BroadcastMessage from 'broadcast-message'

const bmOpts = { 
  channelId: 'cross-domain-demo', 
  
  /* 必须使用可信域下的broadcast-message页来进行消息的中转,后面会介绍如何自定义可信域页面地址 */
  trustedDomainPages: 'https://broadcast-message.anzz.top/dist/pages/broadcast-message.html',

  /* 指定接收来自哪个域名发送过来的消息,*表示信任所有域名的消息,生产环境推荐必须指定具体域名 */
  // targetOrigin: "https:anzz.top"
  targetOrigin: "*"
}

const broadcastMessage = new BroadcastMessage(bmOpts)

/* 注册接收消息事件 */
broadcastMessage.addEventListener("message", event => {
  console.log(`[BroadcastMessage-Event]`, event.data)
})

域名B的代码示例:

import BroadcastMessage from 'broadcast-message'

const bmOpts = {
  channelId: 'cross-domain-demo', 
  
  /* 可信域消息中转页的地址必须和域名A定义的一致 */
  trustedDomainPages: 'https://broadcast-message.anzz.top/dist/pages/broadcast-message.html',
}
const broadcastMessage = new BroadcastMessage(bmOpts)

/* 发送消息 */
broadcastMessage.postMessage(`[cross-domain-demo] ${Date.now()}`)

附注:当进行跨域通信时,需先确认域名A和域名B的页面都已加载,且都完成了broadcast-message的初始化工作,才能开始通信,否则会出现消息发送出去了,但接收方还没准备好等情况,导致消息通信失败

自定义可信域页面

对于有跨域通信需求的项目,加载一个可信域的页面作为消息中转页是必不可少的。默认可以使用项目的自带可信域页面:
https://broadcast-message.anzz.top/dist/pages/broadcast-message.html

但出于下面的原因你可能需要自定义可信域页面

  • 需要更快更稳定的可信域页面
  • 项目对第三方网址有严格的限定
  • 封装内部使用的跨域消息通信库

自定义可信域页面代码示例如下:

import BroadcastMessage from 'broadcast-message'
new BroadcastMessage({
  /* 只需标识当前脚本运行在可信域页面内即可 */
  inTrustedDomainPages: true,
})

将初始化了inTrustedDomainPages选项的页面发布出去,即可获得一个自定义可信域页面

附注:为了让自定义可信域页面更快地加载,你应该务必不要掺杂其他无关逻辑进去,它只作为消息中转页面存在,并不需要其他额外内容

broadcast-message选项

new BroadcastMessage(opts) 中的opts是一个对象它包含的选项和具体说明如下:

const bmOpts = {
  /**
   * 指定BroadcastMessage的消息频道id,不同BroadcastMessage实例,只要channelId一致即可通信
   * 默认的channelId为"*" , 这意味着在不指定频道id的情况下,各个实例的消息都会发往同一个频道上
   */
  channelId: '*',

  /**
   * 允许既是消息的发送页,也可以是消息的接收页,从而做到同源同页收发消息
   * postMessage、BroadcastChannel和storage事件都是必须一个页面发送,另一个页面接收
   * 而BroadcastMessage允许同页面收发消息,只需将allowLocalBroadcast设为true即可
   */
  allowLocalBroadcast: false,

  /**
   * 给 broadcastMessage.addEventListener("message", function (event) {}) 事件派发原始消息对象
   * 默认情况下,broadcastMessage.postMessage(msg) 中的 msg 等于接收到的 event.data,但其实为了能准确派送消息,
   * msg会被包装成有很多辅助参数的对象,如果你需要这些辅助参数信息,可以将emitOriginalMessage设为true
   * 则收到的event.data将是如下的对象实体:
   * event.data = {
   *    data: msg,                   // 消息本体
   *    type: 'BroadcastMessage',    // 消息类型,默认为BroadcastMessage,其他类型,则是自定义消息或内部消息
   *    origin: '',                  // 消息来源的域名地址
   *    targetOrigin: '',            // 消息目标源的域名地址
   *    referrer: '',                // 消息来自哪个页面
   *    timeStamp: '',               // 消息发出时的时间戳
   *    transportType: '',           // 中转消息的载体类型,只可能为BroadcastMessage和localStorage
   *    allowLocalBroadcast: false,  // 该消息是否允许被同页面的BroadcastMessage实例接收到
   *    channelId: '*',              // 消息所属的频道id
   *    instanceId: 'xx_xx',         // 消息所属BroadcastMessage实例id
   *    debug: false                 // 是否为开启了调试选项的消息
   * }
   */
  emitOriginalMessage: false,

  /**
   * 指定消息发送的目标域,规则跟postMessage的targetOrigin一样
   * 但不同的是支持定义数组形式的targetOrigin,从而实现批量跨域数据发送
   * 当然如果是"*"的话就是给任意运行了本插件的页面发送数据
   * 默认为 `location.origin`,即只可以在同域名的页面之间进行通信
   */
  targetOrigin: location.origin,

  /**
   * 指定数据中转传输使用的传输类型,可选值:BroadcastChannel、localStorage
   * 不指定的话,优先使用BroadcastChannel,在不兼容BroadcastChannel的浏览器下使用localStorage
   * 无特需情况,不建议修改该项配置
   */
  transportType: 'BroadcastChannel',

  /**
   * 标识BroadcastMessage实例是否处于可信域的页面上运行
   * 如果是,当前页面作为可信域的中介页嵌入到具体运行环境中
   * 该选项只在自定义可信域页面中使用,其他情况不要使用
   */
  inTrustedDomainPages: false,

  /**
   * 指定作为消息中转的可信域页面地址,有需跨域通信需求才会用到该选项
   * 官方提供的可信域页面地址为:
   * https://broadcast-message.anzz.top/dist/pages/broadcast-message.html
   */
  trustedDomainPages: '',

  /* 标识是否开启调试选项 */
  debug: false,
}

broadcast-message方法

broadcast-message实例包含的方法如下:

  • addEventListener
  • onMessage
  • removeEventListener
  • offMessage
  • postMessage
  • ready
  • close

下面对各个函数的使用方式进行逐一说明:

addEventListener

函数参数:

  • type {String} 必选,必须为 'message'
  • listener {Function} 必选

添加消息事件的侦听器

import BroadcastMessage from 'broadcast-message'
const broadcastMessage = new BroadcastMessage({...opts})

broadcastMessage.addEventListener('message', event => {
  console.log(`[BroadcastMessage-Event]`, event.data)  
})

onMessage

函数参数:

  • handler {Function} 必选

添加消息处理函数,addEventListener是基于onMessage的,只是为了让BroadcastMessage保持跟BroadcastChannel一致的函数调用方式,所以增加了addEventListener事件

import BroadcastMessage from 'broadcast-message'
const broadcastMessage = new BroadcastMessage({...opts})

broadcastMessage.onMessage(event => {
  console.log(`[BroadcastMessage-Event]`, event.data)  
})

removeEventListener

函数参数:

  • type {String} 必选,必须为 'message'
  • listener {Function} 必选

移除消息事件的侦听器

import BroadcastMessage from 'broadcast-message'
const broadcastMessage = new BroadcastMessage({...opts})

const listener = event => { console.log(`[BroadcastMessage-Event]`, event.data) }
broadcastMessage.addEventListener('message', listener)

/* 一段时间后移除消息事件侦听器 */
setTimeout(() => { broadcastMessage.removeEventListener('message', listener) }, 3000)

offMessage

函数参数:

  • handler {Function} 必选

移除消息处理函数,removeEventListener是基于onMessage的,只是为了让BroadcastMessage保持跟BroadcastChannel一致的函数调用方式,所以增加了removeEventListener事件

import BroadcastMessage from 'broadcast-message'
const broadcastMessage = new BroadcastMessage({...opts})

const handler = event => { console.log(`[BroadcastMessage-Event]`, event.data) }
broadcastMessage.onMessage(handler)

/* 一段时间后移除消息事件处理函数 */
setTimeout(() => { broadcastMessage.offMessage(listener) }, 3000)

postMessage

函数参数:

  • message {String|Number|Array|Object} 必选,必须为 'message'
  • messageType {String} 可选 默认为'BroadcastMessage', 内部消息为:'Internal-BroadcastMessage', 一般不需传入该参数

发送消息

import BroadcastMessage from 'broadcast-message'
const broadcastMessage = new BroadcastMessage({...opts})
broadcastMessage.postMessage('test')

ready

函数参数:

  • handler {Function} 必选 BroadcastMessage实例创建就绪后的回调函数

侦听BroadcastMessage实例创建就绪后,再执行响应的回调操作,因为BroadcastMessage初始化会创建消息中转的iframe,需要一定的创建时间,所以并不是拥有了BroadcastMessage实例就可以马上postMessage,而是要等到实例就绪才会将消息传送出去

默认情况下,在执行postMessage前,会自动检测BroadcastMessage实例是否完全就绪,如果没有会将需要发送的消息缓存起来,等BroadcastMessage实例创建就绪之后才将缓存的消息发送出去

import BroadcastMessage from 'broadcast-message'
const broadcastMessage = new BroadcastMessage({...opts})

broadcastMessage.ready(() => {
  broadcastMessage.postMessage('test')
})

// 如果没使用ready函数,直接调用postMessage也是没问题的
// 这是因为BroadcastMessage会自动判断实例是否创建就绪
// broadcastMessage.postMessage('test')

close

函数参数:

需要销毁BroadcastMessage实例时调用的函数,该函数会将BroadcastMessage中转所需的iframe、BroadcastChannel实例和通过addEventListener或onMessage定义的消息函数销毁掉

import BroadcastMessage from 'broadcast-message'
let broadcastMessage = new BroadcastMessage({...opts})

broadcastMessage.onMessage(event => { console.log(`[BroadcastMessage-Event]`, event.data) })
broadcastMessage.postMessage('test')

/* 一段时间后销毁broadcastMessage实例 */
setTimeout(() => { 
  broadcastMessage.close() 
  broadcastMessage = null
}, 3000)

Releases

No releases published

Packages

No packages published