如何优雅的使用postMessage解决页面间通讯

PostMessageEmitter

基于 postMessage 实现的父子页面通讯中心

快速使用

Iframe嵌套

parent.html

<iframe name="ifr" src="./chilren.html" frameborder="0"></iframe>

let emitter = new PostMessageEmitter(ifr)

emitter.on("callFather" , (data)=>{
    console.log(data) // "hello, i am children"
})
emitter.emit("callChildren" , "hello, i am father")

children.html

let emitter = new PostMessageEmitter(window)

emitter.on("callChildren" , (data)=>{
    console.log(data) // "hello, i am father"
})
emitter.emit("callFather" , "hello, i am children")

window.open

parent.html

let emitter = new PostMessageEmitter(window.open(./chilren.html))

emitter.on("callFather" , (data)=>{
    console.log(data) // "hello, i am children"
})
emitter.emit("callChildren" , "hello, i am father")

children.html

let emitter = new PostMessageEmitter(window)

emitter.on("callChildren" , (data)=>{
    console.log(data) // "hello, i am father"
})
emitter.emit("callFather" , "hello, i am children")

特性

  • 简单、统一的Api
  • 支持一父多子

接口

interface IPostMessageEmiiter {
    (el: Window | HTMLIFrameElement, domain: string): Emitter
    on(eventName: string, cb: Function): void,
    emit(eventName: string, data: any): void,
    destroy(): void,
}

源码

function PostMessageEmitter(el, domain = "*") {
    if (!(this instanceof PostMessageEmitter)) {
        throw new TypeError("just support new invoke");
    }

    let currentWin = window;
    let targetWin = null
    let identity = null
    if (currentWin === el) {
        if (window.opener) {
            targetWin = el.opener
        } else {
            targetWin = el.parent
        }
        identity = currentWin.identity
    }
    else if (el.tagName && el.tagName.toLowerCase() == "iframe" && el.contentWindow) {
        targetWin = el.contentWindow;
        targetWin.identity = identity = uuid()
    }
    else if (currentWin === el.opener) {
        targetWin = el
        targetWin.identity = identity = uuid()
    }

    if (!targetWin) {
        throw new TypeError("argument[0] must be iframe or window");
    }

    let events = {};

    const handleMessage = (e) => {
        if (domain != "*" && e.origin !== domain) {
            return;
        }
        let { type, data, _identity } = e.data || {};
        if (identity !== _identity) {
            return
        }
        let cbs = events[type] || [];
        cbs.reduce((prev, cb) => {
            cb && cb(prev);
            return prev;
        }, data);
    };
    window.addEventListener("message", handleMessage);
    return {
        emit(type, data) {
            targetWin.postMessage(
                {
                    type,
                    data,
                    _identity: identity
                },
                domain
            );
        },
        on(type, cb) {
            if (typeof cb != "function") {
                throw new TypeError("argument[1] must a function!");
            }
            if (!events[type]) {
                events[type] = [];
            }
            events[type].push(cb);
        },
        destroy() {
            window.removeEventListener("message", handleMessage);
            targetWin = null;
        },
    };
}

function uuid() {
    function S4() {
        return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
    }
    return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4());
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值