broadcast-message
Version:
基于postMessage+BroadcastChannel+localStorage+互信域名的前端页面数据通信解决方案
383 lines (381 loc) • 17.5 kB
JavaScript
var l = Object.defineProperty;
var f = (o, e, s) => e in o ? l(o, e, { enumerable: !0, configurable: !0, writable: !0, value: s }) : o[e] = s;
var i = (o, e, s) => (f(o, typeof e != "symbol" ? e + "" : e, s), s);
/*!
* @name url.js
* @description 用于对url进行解析的相关方法
* @version 0.0.1
* @author Blaze
* @date 27/03/2019 15:52
* @github https://github.com/xxxily
*/
function c(o) {
var e = document.createElement("a");
return e.href = o || window.location.href, {
source: o,
protocol: e.protocol.replace(":", ""),
host: e.hostname,
port: e.port,
origin: e.origin,
search: e.search,
query: e.search,
file: (e.pathname.match(/\/([^/?#]+)$/i) || ["", ""])[1],
hash: e.hash.replace("#", ""),
path: e.pathname.replace(/^([^/])/, "/$1"),
relative: (e.href.match(/tps?:\/\/[^/]+(.+)/) || ["", ""])[1],
params: function() {
const s = {}, a = [];
for (var _ = e.search.replace(/^\?/, "").split("&"), t = 0; t < _.length; t++) {
var n = _[t];
n !== "" && n.indexOf("=") && a.push(n);
}
for (var r = 0; r < a.length; r++) {
var u = a[r], d = u.indexOf("="), g = u.substring(0, d), h = u.substring(d + 1);
g ? s[g] = h : s[h] = null;
}
return s;
}()
};
}
function w(o) {
var e = [];
if (Object.prototype.toString.call(o) !== "[object Object]")
return "";
for (var s in o)
if (Object.hasOwnProperty.call(o, s)) {
var a = o[s], _ = Object.prototype.toString.call(a);
if (a === "" || _ === "[object Undefined]")
continue;
a === null ? e.push(s) : _ === "[object Array]" ? e.push(s + "=" + a.join(",")) : (a = (JSON.stringify(a) || "" + a).replace(/(^"|"$)/g, ""), e.push(s + "=" + a));
}
return e.join("&");
}
function m(o) {
var e = w(o.params) || "";
e && (e = "?" + e);
var s = o.hash ? "#" + o.hash : "";
return o.origin + o.path + e + s;
}
/*!
* @name BroadcastMessage.js
* @description 基于postMessage+BroadcastChannel+localStorage+互信域名的前端页面数据通信解决方案
* @version 0.0.1
* @author xxxily
* @date 2022/11/07 09:26
* @github https://github.com/xxxily
*/
class I {
constructor(e = {}) {
i(this, "debug");
i(this, "targetOrigin");
i(this, "transportType");
i(this, "inTrustedDomainPages");
i(this, "allowLocalBroadcast");
i(this, "channelId");
i(this, "instanceId");
i(this, "emitOriginalMessage");
i(this, "readyTime");
i(this, "messageWindow");
i(this, "trustedDomainPages");
i(this, "__hasRegisterPostMessageListener__");
i(this, "__BroadcastChannelInstance__");
i(this, "__hasRegisterStorageListener__");
i(this, "__readyHandler__");
i(this, "_readyStartTime_");
i(this, "_isReady_");
i(this, "__messageListener__");
i(this, "__internalMessageListener__");
i(this, "__hasInternalMessageListener__");
i(this, "__hasMessageListener__");
i(this, "_message_cache_");
this.targetOrigin = e.targetOrigin || location.origin, this.transportType = e.transportType || "BroadcastChannel", this.inTrustedDomainPages = e.inTrustedDomainPages || !1, this.trustedDomainPages = e.trustedDomainPages || "", this.allowLocalBroadcast = e.allowLocalBroadcast || !1, this.channelId = String(e.channelId || "*"), this.instanceId = this.channelId + "_" + (window.performance ? performance.now() : Date.now()), this.debug = e.debug || !1, this.emitOriginalMessage = e.emitOriginalMessage || !1, this.messageWindow = window, this.readyTime = 0, this._isReady_ = !1, this.__messageListener__ = [], this.__internalMessageListener__ = [], this.init();
}
init() {
if (this.trustedDomainPages) {
const e = c(this.trustedDomainPages);
e.params.channelId = this.channelId, e.params.instanceId = this.instanceId, this.trustedDomainPages = m(e);
}
this.__registerMessageWindow__(), this.inTrustedDomainPages && window.top && window !== window.top.window && (this.__registerPostMessageListener__(), this.__registerStorageMessageListener__(), this.__registerBroadcastChannelListener__(), this.__sendMessageToParentWindow__("initReady"));
}
getTrustedDomain() {
return this.trustedDomainPages ? c(this.trustedDomainPages).origin : location.origin;
}
__sendMessageToParentWindow__(e) {
if (window.parent === window || !e)
return;
const s = window.__broadcastMessageChannelId__ || this.channelId, a = window.__broadcastMessageInstanceId__ || this.instanceId;
window.parent.postMessage(
{
data: e,
channelId: s,
instanceId: a,
type: "Internal-BroadcastMessage"
},
"*"
);
}
__registerMessageWindow__() {
if (this.messageWindow !== window)
return;
const e = document.createElement("iframe");
if (e.style.display = "none", e.style.visibility = "hidden", e.src = this.trustedDomainPages, e.className = "broadcast-message-iframe", document.body.appendChild(e), this.messageWindow = e.contentWindow, !this.inTrustedDomainPages && !this.trustedDomainPages && this.messageWindow && this.messageWindow.document) {
const s = this.messageWindow.document;
if (!s.body) {
const a = s.createElement("body");
a.innerHTML = "<h1>Broadcast-Message-page</h1>", s.documentElement.appendChild(a);
}
s.open(), s.write(`
<script>
function ${this.__registerPostMessageListener__};
function ${this.__registerStorageMessageListener__};
function ${this.__registerBroadcastChannelListener__};
function ${this.__sendMessageToParentWindow__};
function init () {
if (window.__hasInit__) { return false; }
window.__broadcastMessageChannelId__ = "${this.channelId}";
window.__broadcastMessageInstanceId__ = "${this.instanceId}";
__registerPostMessageListener__();
__registerStorageMessageListener__();
__registerBroadcastChannelListener__();
__sendMessageToParentWindow__('initReady');
window.__hasInit__ = true ;
}
document.addEventListener("DOMContentLoaded", init);
window.addEventListener("load", init)
setTimeout(init, 100)
<\/script>
`), s.close();
}
}
__registerPostMessageListener__() {
const e = this;
if (e.__hasRegisterPostMessageListener__ || window.top === window)
return !1;
if (window.__windowId__ = String((Date.now() - Math.random() * 1e5).toFixed(2)), !window.__broadcastMessageChannelId__)
try {
const t = c(location.href);
if (t.params.channelId && t.params.instanceId)
window.__broadcastMessageChannelId__ = t.params.channelId, window.__broadcastMessageInstanceId__ = t.params.instanceId;
else
throw new Error("URL\u7F3A\u5931\u76F8\u5173\u53C2\u6570");
} catch (t) {
console.error(`[registerPostMessageListener][${location.origin}] \u83B7\u53D6broadcastMessageChannelId\u5931\u8D25`, t);
}
function s() {
let t = document.querySelector("#message-transport-iframe");
return t || (t = document.createElement("iframe"), t.id = "message-transport-iframe", t.style.display = "none", t.style.visibility = "hidden", document.body.appendChild(t), t);
}
function a(t) {
const n = t.data;
n.windowId = window.__windowId__, n.debug && console.log(`[transportMessage][iframe][${location.origin}]`);
const r = s().contentWindow;
if (!r)
throw new Error("[transportMessage][iframe]\u7528\u4E8E\u6D88\u606F\u4E2D\u8F6C\u7684iframe\u4E0D\u5B58\u5728\uFF0C\u68C0\u6D4B\u662F\u5426\u610F\u5916\u5220\u9664\u4E86");
if (r.BroadcastChannel && r.BroadcastChannel.prototype.postMessage && n.transportType !== "localStorage") {
const d = r.__BroadcastChannelInstance__ || new r.BroadcastChannel("__BroadcastChannelMessage__");
r.__BroadcastChannelInstance__ = r.__BroadcastChannelInstance__ || d, d.postMessage(n);
} else
r.localStorage.setItem("__BroadcastMessage__", JSON.stringify(n));
}
s();
function _(t) {
const n = t.data;
if (!n)
return !1;
if (n.data === "readyTest") {
window.__broadcastMessageReadyInfo__ = n;
const r = e.__sendMessageToParentWindow__ || window.__sendMessageToParentWindow__;
r instanceof Function && r("ready");
}
}
window.addEventListener(
"message",
(t) => {
const n = t.data;
if (!n || !n.type)
return !1;
n.type === "BroadcastMessage" ? a(t) : n.type === "Internal-BroadcastMessage" && _(t);
},
!0
), this.__hasRegisterPostMessageListener__ = !0;
}
__registerBroadcastChannelListener__() {
if (!window.BroadcastChannel || !BroadcastChannel.prototype.postMessage)
return console.error(`[BroadcastChannel][${location.origin}]`, "\u4E0D\u652F\u6301BroadcastChannel"), !1;
if (this.__BroadcastChannelInstance__)
return !0;
const e = new BroadcastChannel("__BroadcastChannelMessage__");
e.addEventListener("message", (s) => {
const a = s.data;
if (!a || !a.windowId || !a.data)
return !1;
const _ = window.__broadcastMessageChannelId__;
if (!a.channelId || _ && a.channelId !== _)
return a.debug && console.info("[transportMessage] channelId\u4E0D\u5B58\u5728\u6216\u4E0D\u5339\u914D\uFF0C\u7981\u6B62\u6570\u636E\u5411\u4E0A\u4F20\u9012", _, a), !1;
if (!a.allowLocalBroadcast && window.__windowId__ && window.__windowId__ === a.windowId)
return a.debug && console.info("[BroadcastChannel-event] \u6D88\u606F\u6E90\u63A5\u6536\u7AEF\u548C\u6D88\u606F\u6E90\u7684\u6765\u6E90\u7AEF\u4E00\u81F4\uFF0C\u7981\u6B62\u6570\u636E\u5411\u4E0A\u4F20\u9012"), !1;
(Array.isArray(a.targetOrigin) ? a.targetOrigin : [a.targetOrigin]).forEach((n) => {
const r = window.__broadcastMessageReadyInfo__ || null;
if (n !== "*" && (!r || !r.referrer.startsWith(n)))
return a.instanceId !== r.instanceId && a.debug && console.warn(
`[BroadcastChannel-event] \u6D88\u606F\u7684targetOrigin\u548C\u5F53\u524D\u7236\u9875\u9762\u7684\u5730\u5740\u4E0D\u5339\u914D\uFF0C\u53D6\u6D88\u6570\u636E\u5411\u4E0A\u4F20\u9012\uFF0C[targetOrigin]${n} [parent page]${r.referrer}`
), !1;
window.parent.postMessage(a, n);
});
}), this.__BroadcastChannelInstance__ = e;
}
__registerStorageMessageListener__() {
if (this.__hasRegisterStorageListener__)
return !1;
window.addEventListener("storage", (e) => {
let s = e.newValue;
if (!s || e.key !== "__BroadcastMessage__")
return !1;
try {
s = JSON.parse(s);
} catch {
return !1;
}
if (!s || !s.windowId || !s.data)
return !1;
const a = window.__broadcastMessageChannelId__;
if (!s.channelId || a && s.channelId !== a)
return s.debug && console.error("[transportMessage] channelId\u4E0D\u5B58\u5728\u6216\u4E0D\u5339\u914D\uFF0C\u7981\u6B62\u6570\u636E\u5411\u4E0A\u4F20\u9012", a, s), !1;
if (s.debug && console.log(`[storage-event][iframe][${location.origin}]`, e), !s.allowLocalBroadcast && window.__windowId__ && window.__windowId__ === s.windowId)
return s.debug && console.info("[storage-event] \u6D88\u606F\u6E90\u63A5\u6536\u7AEF\u548C\u6D88\u606F\u6E90\u7684\u6765\u6E90\u7AEF\u4E00\u81F4\uFF0C\u7981\u6B62\u6570\u636E\u5411\u4E0A\u4F20\u9012"), !1;
(Array.isArray(s.targetOrigin) ? s.targetOrigin : [s.targetOrigin]).forEach((t) => {
const n = window.__broadcastMessageReadyInfo__ || {
referrer: ""
};
if (t !== "*" && !n.referrer.startsWith(t))
return s.instanceId !== n.instanceId && s.debug && console.warn(
`[storage-event] \u6D88\u606F\u7684targetOrigin\u548C\u5F53\u524D\u7236\u9875\u9762\u7684\u5730\u5740\u4E0D\u5339\u914D\uFF0C\u53D6\u6D88\u6570\u636E\u5411\u4E0A\u4F20\u9012\uFF0C[targetOrigin]${t} [parent page]${n.referrer}`
), !1;
window.parent.postMessage(s, t);
});
}), this.__hasRegisterStorageListener__ = !0;
}
postMessage(e, s) {
if (!this._isReady_ && s !== "Internal-BroadcastMessage")
return this._message_cache_ || this.ready(() => {
Array.isArray(this._message_cache_) && (this._message_cache_.forEach((t) => {
this.postMessage(t);
}), delete this._message_cache_);
}), this._message_cache_ = this._message_cache_ || [], this._message_cache_.push(e), !0;
const a = {
data: e,
type: s || "BroadcastMessage",
origin: location.origin || window.top && window.top.location.origin,
targetOrigin: this.targetOrigin,
referrer: location.href || window.top && window.top.location.href,
timeStamp: window.performance ? performance.now() : Date.now(),
transportType: this.transportType,
allowLocalBroadcast: this.allowLocalBroadcast,
channelId: this.channelId,
instanceId: this.instanceId,
debug: this.debug
};
if (!this.messageWindow || !this.messageWindow.postMessage)
return this.debug && console.error("[messageWindow error] \u65E0\u6CD5\u53D1\u9001message", a, this.messageWindow), !1;
const _ = this.getTrustedDomain();
this.messageWindow.postMessage(a, _);
}
onMessage(e) {
if (this.__messageListener__ = this.__messageListener__ || [], e instanceof Function && !this.__messageListener__.includes(e) && this.__messageListener__.push(e), this.__hasMessageListener__)
return !1;
this.__hasMessageListener__ = !0, window.addEventListener(
"message",
(s) => {
const a = s.data;
if (!(a && a.type === "BroadcastMessage" && a.data && a.channelId && a.referrer))
return !1;
if (this.channelId !== "*" && a.channelId !== this.channelId)
return a.origin !== location.origin && a.debug && console.error("[messageListener] \u5B58\u5728\u6570\u636E\u5B89\u5168\u9690\u60A3\uFF0C\u8BF7\u5B8C\u5584\u811A\u672C\u903B\u8F91", this.channelId, s), !1;
const t = {};
try {
for (const n in s) {
let r = s[n];
n === "data" && !this.emitOriginalMessage ? r = a.data : n === "type" && (r = "BroadcastMessage"), Object.defineProperty(t, n, {
enumerable: n === "data",
writable: !1,
configurable: !0,
value: r
});
}
} catch {
}
this.__messageListener__.forEach((n) => {
n instanceof Function && n(t);
});
},
!0
);
}
offMessage(e) {
this.__messageListener__ = this.__messageListener__ || [];
const s = [];
this.__messageListener__.forEach((a) => {
a !== e && s.push(a);
}), this.__messageListener__ = s;
}
postMessageToInternal(e) {
this.postMessage(e, "Internal-BroadcastMessage");
}
onInternalMessage(e) {
if (this.__internalMessageListener__ = this.__internalMessageListener__ || [], e instanceof Function && !this.__internalMessageListener__.includes(e) && this.__internalMessageListener__.push(e), this.__hasInternalMessageListener__)
return !1;
this.__hasInternalMessageListener__ = !0, window.addEventListener(
"message",
(s) => {
const a = s.data;
return a && a.type === "Internal-BroadcastMessage" && a.channelId === this.channelId && a.instanceId === this.instanceId ? (this.__internalMessageListener__.forEach((t) => {
t instanceof Function && t(s);
}), !0) : !1;
},
!0
);
}
offInternalMessage(e) {
this.__internalMessageListener__ = this.__internalMessageListener__ || [];
const s = [];
this.__internalMessageListener__.forEach((a) => {
a !== e && s.push(a);
}), this.__internalMessageListener__ = s;
}
addEventListener(e, s) {
if (e !== "message")
return !1;
this.onMessage(s);
}
removeEventListener(e, s) {
if (e !== "message")
return !1;
this.offMessage(s);
}
ready(e) {
if (this._isReady_)
return e instanceof Function && e(!0), !0;
if (!this.__readyHandler__) {
this._readyStartTime_ = Date.now(), this.__readyHandler__ = [];
const s = (a) => {
const _ = a.data;
_.data === "initReady" ? this.postMessageToInternal("readyTest") : _.data === "ready" && (this._isReady_ = !0, this._readyStartTime_ && (this.readyTime = Date.now() - this._readyStartTime_), delete this._readyStartTime_, this.debug && console.log(`[BroadcastMessage][ready] \u8017\u65F6\uFF1A${this.readyTime}`), Array.isArray(this.__readyHandler__) && this.__readyHandler__.forEach((t) => {
t instanceof Function && t(!0);
}), delete this.__readyHandler__, this.offInternalMessage(s));
};
this.onInternalMessage(s);
}
if (e instanceof Function)
this.__readyHandler__.push(e);
else
return new Promise((s) => {
Array.isArray(this.__readyHandler__) && this.__readyHandler__.push(s);
});
}
close() {
this.__BroadcastChannelInstance__ && this.__BroadcastChannelInstance__.close && this.__BroadcastChannelInstance__.close(), this.messageWindow && this.messageWindow instanceof HTMLElement && document.body.removeChild(this.messageWindow), this.__messageListener__ = [], this.__readyHandler__ = [];
}
}
export {
I as default
};