WHY

iframe 提供了浏览器原生的硬隔离方案,不论是样式隔离、js 隔离等问题统统都能被完美解决。

所以,如果我们希望集成多个站点,iframe 不失为实现 微前端 的一种简单方式。

但是,url 不同步会是个比较头疼的问题。举个例子,iframe 子窗体内触发 src 变更后,地址栏不会跟随变更。如果用户刷新,则仍会回到之前的页面,比如:

示例 如下,子窗体引入了百度页面,搜索某关键字后跳转结果页,但是刷新后仍会是百度首页。

reactIFrameSolution
reactIFrameSolution

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 ?