我们使用过vue,了解两种常用的通信模式:观察者和发布 / 订阅模式。两者最主要的区别是一对多单向通信还是多对多双向通信的问题。使用我们要确定多个子应用之间互相是否需要通信,来决定使用哪种通信方式。
在微前端中往往需要实现多对多的双向通信模式,例如微应用之间实现通信,主应用和微应用之间实现通信,因此使用发布 / 订阅模式是一种不错的选择。
因为要克服不在同一执行的上下文与跨域问题,我们选择postMessage
实现跨域通信。
demo案例如下
// 主应用
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>主应用</title>
</head>
<body>
<br />
<iframe id="micro1" src="<%= micro1 %>"></iframe>
<iframe id="micro2" src="<%= micro2 %>"></iframe>
<script>
const micro1 = document.getElementById("micro1");
const micro2 = document.getElementById("micro2");
// 等待 iframe 加载完毕后才能通信
micro1.onload = () => {
// 给子应用发送消息,注意明确 targetOrigin
micro1.contentWindow.postMessage("main", "<%= micro1 %>");
};
micro2.onload = () => {
micro2.contentWindow.postMessage("main", "<%= micro2 %>");
};
// 接收来自于 iframe 的消息
window.addEventListener("message", (data) => {
// 通过 data.origin 来进行应用过滤
if (
data.origin === "<%= micro1 %>" ||
data.origin === "<%= micro2 %>"
) {
console.log("main: ", data);
}
});
</script>
</body>
</html>
<!-- micro1.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>micro1</title>
</head>
<body>
<h1>micro1</h1>
<script>
window.addEventListener("message", (data) => {
console.log("micro1: ", data);
if (data.origin === "<%= mainUrl %>") {
window.parent.postMessage("micro1", "<%= mainUrl %>");
}
});
</script>
</body>
</html>
<!-- micro2.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>micro1</title>
</head>
<body>
<h1>micro1</h1>
<script>
window.addEventListener("message", (data) => {
console.log("micro2: ", data);
if (data.origin === "<%= mainUrl %>") {
window.parent.postMessage("micro2", "<%= mainUrl %>");
}
});
</script>
</body>
</html>
以下浓缩操作即为ifream基础通信
父传子
// 等待 子 加载完毕后才能通信
// 父操作
子.contentWindow.postMessage("消息", "指定哪些窗口能接收到消息事件");
// 子操作
window.addEventListener("message", (data) => {
console.log("micro1: ", data);
}
子传父
// 子操作
window.parent.postMessage("消息", "指定哪些窗口能接收到消息事件")
// 父操作
window.addEventListener("message", (data) => {
if (data.origin === "指定哪些窗口能接收到消息事件") {
console.log("main: ", data);
}
}