| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 |
- /**
- * 跨域页面消息通信
- * <pre>
- * 作者:hugh zhuang
- * 邮箱:3378340995@qq.com
- * 日期:2018-07-02-下午3:29:34
- * 版权:广州流辰信息技术有限公司
- * </pre>
- */
- import Storage from './storage'
- export const MESSAGE_TYPE = '__ibps_WEB_MESSAGER__'
- /**
- * 跨域页面消息通信类
- * @class
- */
- export default class Messager {
- /**
- *
- * @param {object} options 实例化参数选项
- * @param {Window} [options.target] 通信目标,使用桥通讯时可以不传
- * @param {string} [options.bridge] 目标桥页面url,如需要跨浏览器窗口传输数据,必须要设置
- * @param {string} [options.origin] 自身的桥,目标可以通过桥联系到自己
- * @param {function} [options.ready] 桥页面加载完成时回调函数
- */
- constructor(options) {
- const o = (this.options = {
- target: window,
- bridge: null,
- origin: null,
- ready: null,
- ...options
- })
- /**
- * 通信目标对象
- * @property {Window} target
- * @type {Window | *}
- */
- this.target = o.target
- this.handlers = {}
- this.proxyBridgeHandler = this.bridgeHandler.bind(this)
- window.addEventListener('storage', this.proxyBridgeHandler)
- if (o.bridge) {
- this.buildBridge().then((el) => {
- this.target = el.contentWindow
- this.el = el
- o.ready && o.ready(this)
- })
- } else {
- o.ready && o.ready(this)
- }
- }
- /**
- * 侦听消息
- * @param {string} type 消息类型
- * @param {Function} handler 处理函数
- */
- on(type, handler) {
- const listener = function (evt) {
- // message: {type:'XDH_WEB_MESSAGER', data:{type:'事件类型', data:{真正发送的数据},bridge:'' }}
- const message = evt.data || {}
- if (!message) return
- if (message.type === MESSAGE_TYPE && message.data.type === type) {
- handler(message.data.data, message.data.bridge)
- }
- }
- if (!this.handlers[type]) {
- this.handlers[type] = []
- }
- this.handlers[type].push(listener)
- window.addEventListener('message', listener)
- }
- /**
- * 取消侦听
- * @param {string} [type] 消息类型
- * @param {Function} [handler] 处理函数
- */
- off(type, handler) {
- // 制定类型和事件句柄
- if (type && handler) {
- const handlers = this.handlers[type] || []
- handlers.forEach((h, index) => {
- if (handler === h) {
- handlers.splice(index, 1)
- window.removeEventListener('message', h)
- }
- })
- // 制定事件类型
- } else if (type) {
- const handlers = this.handlers[type] || []
- handlers.forEach((h) => {
- window.removeEventListener('message', h)
- })
- delete this.handlers[type]
- // 全不制定,全部取消侦听
- } else {
- Object.keys(this.handlers).forEach((key) => {
- this.off(key)
- })
- }
- }
- /**
- * 发送消息
- * @param {string} [type] 消息类型
- * @param {Object} [data] 发送数据
- */
- fire(type, data) {
- if (!this.target) return
- const message = {
- type: MESSAGE_TYPE,
- data: {
- type,
- data,
- bridge: this.options.origin
- }
- }
- // 桥转发时,数据包多一层
- if (this.options.bridge) {
- const bridge = {
- type: MESSAGE_TYPE,
- data: message
- }
- this.target.postMessage(bridge, '*')
- } else {
- this.target.postMessage(message, '*')
- }
- }
- /**
- * 接收消息,只接收一次
- * @param {string} [type] 消息类型
- * @param {Function} [handler] 处理函数
- */
- once(type, handler) {
- this.on(type, () => {
- handler.apply(this, arguments)
- this.off(type, handler)
- })
- }
- /**
- * 搭桥,创建iframe,加载桥页面
- * @return {Promise<any>}
- */
- buildBridge() {
- return new Promise((resolve, reject) => {
- const el = document.createElement('iframe')
- el.style.display = 'none'
- el.setAttribute('src', this.options.bridge + '?t=' + new Date().getTime())
- el.onload = () => {
- resolve(el)
- }
- el.onerror = (e) => {
- reject(e)
- }
- document.body.appendChild(el)
- })
- }
- /**
- * 桥页面传输写入localStorage
- * @param {object} message
- * @param {string} origin 消息来源
- */
- pass(message) {
- Storage.set(MESSAGE_TYPE, {
- message: message,
- __t__: new Date().getTime()
- })
- }
- /**
- * localStorage 事件处理函数
- * @param {object} evt
- */
- bridgeHandler(evt) {
- if (evt.key !== MESSAGE_TYPE) return
- const value = JSON.parse(evt.newValue)
- if (value && value.message) {
- const message = value.message
- const handlers = this.handlers[message.type] || []
- handlers.forEach((handler) => {
- handler({
- data: {
- type: MESSAGE_TYPE,
- data: message
- }
- })
- })
- }
- }
- /**
- * 销毁
- */
- destroy() {
- this.off()
- if (this.proxyBridgeHandler) {
- window.removeEventListener('storage', this.proxyBridgeHandler)
- }
- if (this.el) {
- this.el.parentNode.removeChild(this.el)
- }
- }
- }
|