WHY
iframe 提供了浏览器原生的硬隔离方案,不论是样式隔离、js 隔离等问题统统都能被完美解决。
所以,如果我们希望集成多个站点,iframe 不失为实现 微前端 的一种简单方式。
但是,url 不同步会是个比较头疼的问题。举个例子,iframe 子窗体内触发 src 变更后,地址栏不会跟随变更。如果用户刷新,则仍会回到之前的页面,比如:
示例 如下,子窗体引入了百度页面,搜索某关键字后跳转结果页,但是刷新后仍会是百度首页。
HOW
上例的百度站点不同域,我们无法做到改变父窗体地址栏。但,若是自有站点,子窗体同域,我们可以通过代码实现更新父窗体地址栏,这样可变相解决 URL不同步等操作父窗体的问题。解决方案参考如下:
跨域 =》同域
通常自有站点可接受多个二级域名,如 image.baidu.com 和 zhidao.baidu.com;默认 document.domain 是不同的,但我们可 js 设置 document.domain = baidu.com
来实现同域。
前提条件:这两个域名必须属于同一个基础域名,且所用的协议,端口都要一致
出于对安全性的考虑,而禁止两个或者多个不同域的页面进行互相操作。
相同域的页面在相互操作的时候不会有任何问题。
监听子窗体
直接上代码,参考如下:
import React, { useState, useRef } from 'react';
type Func = (...args: any[]) => any;
interface SubIFrameProps {
suburl: string;
checkPathForMenu: Func;
}
const SubIFrame: React.FunctionComponent<SubIFrameProps> = (props: SubIFrameProps) => {
const [iFrameHeight, SetIFrameHeight] = useState('0px');
const iframeEl = useRef(null);
function replaceParentURL(href:string) {
console.log(href);
//do sth. e.g. window.history.pushState( ... )
//props.checkPathForMenu(href);
}
return (
<iframe
id="subapp-iframe"
style={{ width: '100%', height: iFrameHeight, overflow: 'visible' }}
onLoad={() => {
SetIFrameHeight(`${window.innerHeight}px`);
try {
const currentIFrameWindow = (iframeEl as any).current.contentWindow;
currentIFrameWindow.window.onhashchange = () => {
const currentVmHref = currentIFrameWindow.location.href;
console.log("currentVmHref: " + currentVmHref);
replaceParentURL(currentVmHref);
};
} catch (error) {
console.log(error);
}
}}
title="subAPP"
ref={iframeEl}
src={props.suburl}
width="100%"
height={iFrameHeight}
scrolling="no"
frameBorder="0"
/>
);
}
export default SubIFrame;
列出相关的点:
- iframe onload 处理 iframe 载入事件
- react hooks useRef 关联 iframe(低版本 react 写法会不同)
- window.onhashchange 监听子窗体更改
- 扩展:如代码中
props.checkPathForMenu
方法的调用可实现父窗体函数(笔者站点实现更改菜单选中项)
有关 iframe 样式的更改可参考:如何在页面中优雅地内嵌 iframe ?