Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

加载完子应用入口html后为何还要请求一次主应用host #54

Open
handoing opened this issue Aug 12, 2022 · 28 comments
Open
Labels
good question Good for newcomers

Comments

@handoing
Copy link

描述bug
加载完子应用入口html后为何还要请求一次主应用host

@yiludege yiludege added the good question Good for newcomers label Aug 12, 2022
@yiludege
Copy link
Collaborator

yiludege commented Aug 12, 2022

这是一个很好的问题,目前也是无界的一个无法解决的点。

1、子应用需要一个 空白的、同域的、浏览器前进后退可以生效的 iframe 作为沙箱。

2、无界采用直接加载 iframe src 等于主应用 host 的地址,然后等 window.location 初始化成主应用域名后(为了子应用路由 window.history.pushState 可以正常工作)后立即停止iframe的加载,此时子应用跳转路由后点击浏览器后退也可以生效到子应用

3、但是在等待 location origin 初始化的过程中有可能加载了主应用 host 地址的 html,然后运行了这个地址内的部分代码,在 safari 浏览器中尤其明显:

  • iframe 加载主应用 host 以及资源

image

  • iframe 加载主应用 host 和资源后运行了主应用的js

image

无界框架做了兜底方案,发现主应用代码在子应用中运行会直接抛错中断运行

4、 location origin 一旦完成初始化,无界会清空这个iframe,注入子应用的代码进行执行

所以现在的问题是如何得到一个空白的、同域的、浏览器前进后退可以生效的 iframe

方案 1:主应用提供一个路径比如说 https://host/empty ,这个路径返回不包含任何内容,子应用设置 attr{src:'https://host/empty'},这样 iframe 的 src 就是 https://host/empty,不会存在污染问题, 阿里 Alfa也有类似方案

方案2:通过纯粹的 js 实现 一个 空白的、同域的、浏览器前进后退可以生效的 iframe,详见讨论

@yiludege
Copy link
Collaborator

yiludege commented Nov 23, 2022

大家如果有好的方案的话可以在这个issue提供一下,下面是背景介绍

介绍:

1、无界微前端采用 iframe 作为沙箱,需要一个空白、主应用同域、不运行主应用 js 代码的 iframe

2、子应用在 iframe 内部运行,当子应用跳转路由时会调用window.history.pushState、window.history.replaceState 等方法,如果 iframe 没有设置 src 的话其 location 为 about:blank 会报跨域错误,因为 location.origin 是 about:blank,而跳转过去的路由是具体的 url

3、无界一般设置 iframe 的 src 为主应用的 host,劫持子应用调用window.history.pushState、window.history.replaceState,将跳转的 url 的域名替换成主应用的 host,这样子应用路由就可以正常跳转

4、但是设置 iframe 的 src 为主应用的 host 会导致 iframe 加载、运行主应用的 js 资源污染了 iframe 沙箱

5、无界这边是采用轮询的方式一旦 iframe 的 location 初始化完成就立即停止加载,但还是无法避免污染问题

具体内容:

1、实现一个空白、主应用同域、不运行主应用 js 代码的 iframe

2、子应用运行在 iframe 沙箱内,浏览器前进后退可以作用到子应用

目前尝试:

1、将 iframe 的src 设置为 主应用的 host

2、将 iframe 插入到 dom 之后立即运行 iframe.contentDocument.open();iframe.contentDocument.write("");iframe.contentDocument.close(),可以得到一个空白、主应用同域、不运行主应用 js 代码的 iframe,但是浏览器前进后退无法作用到子应用

@gyhyfj
Copy link

gyhyfj commented May 13, 2023

可否让iframe加载主应用host时请求头上带一个特殊字段,然后主应用如果发现这样的特殊字段就返回个空的txt之类的

@taoguili
Copy link

taoguili commented Jun 5, 2023

目前的轮询实测经常遇到因为无法及时中止iframe加载,而开始无限嵌套iframe的问题,导致页面无法加载直至卡死,这时控制台会不断输出中止执行的警告。

iframe初始化时候会向主域发出一个请求,这次请求的请求头 referer 字段是主域名,可否通过配置主应用,如果检测到这个referer字段,就返回一个特殊的文件,这样似乎就不必轮询了,也可以通过这个特殊文件里的一段js代码,调用提前放在全局的一个函数,主动通知无界iframe已经准备好了


老板,轮询这个有处理吗?

@dorasong
Copy link

各位大大们,这个问题有解决吗?目前使用无界遇到同样问题了,然后页面卡死了,子应用无法加载,页面一直在loading @所有人

@dorasong
Copy link

目前的轮询实测经常遇到因为无法及时中止iframe加载,而开始无限嵌套iframe的问题,导致页面无法加载直至卡死,这时控制台会不断输出中止执行的警告。

iframe初始化时候会向主域发出一个请求,这次请求的请求头 referer 字段是主域名,可否通过配置主应用,如果检测到这个referer字段,就返回一个特殊的文件,这样似乎就不必轮询了,也可以通过这个特殊文件里的一段js代码,调用提前放在全局的一个函数,主动通知无界iframe已经准备好了

请问有解决相关问题吗?

@wang8254
Copy link

wang8254 commented Aug 3, 2023

目前的轮询实测经常遇到因为无法及时中止iframe加载,而开始无限调用iframe的问题,导致页面无法加载请求卡死,随后控制台会不断输出中止执行的警告。
iframe初始化的时候会向主域发出一个请求,这次请求的请求头referer字段是主域名,可以通过配置主应用否决,如果检测到这个referer字段,就返回一个特殊的文件,这样好像就不需要轮询了,也可以通过这个特殊文件里的js代码,调用提前放在全局的一个函数,主动通知无界iframe已经准备好了

请问有解决相关问题吗?

同问,如果暂时没有好的办法,有没有什么方法让它提示一次就行了。或者配置一个属性什么的。不让无限提示

@JanusSpark
Copy link

子应用如何设置attr?看文档 我一直感觉子应用没啥设置的地方啊

@pandaOreo
Copy link

attr在哪设置

@enmotion
Copy link

这个坑有点大啊,具体怎么解决,能给出范例代码吗?这么看说明,很晦涩

@pandaOreo
Copy link

pandaOreo commented Nov 28, 2023 via email

@Lemonreds
Copy link

关注~

@baixi233

This comment was marked as outdated.

@li1037
Copy link

li1037 commented Jan 11, 2024

各位大大们,这个问题有解决吗?目前使用无界遇到同样问题了,然后页面卡死了,子应用无法加载,页面一直在loading @所有人

@dorasong 大佬这个解决了吗,我也遇到一样的问题,/host/empty返回空白内容后,一直显示loading中,也没报错

@pandaOreo
Copy link

pandaOreo commented Jan 11, 2024 via email

@AttackXiaoJinJin
Copy link
Contributor

@JanusSpark @pandaOreo 传props:https://wujie-micro.github.io/doc/api/startApp.html#attrs

await startApp({
        ...props,
        attrs:{
          // 主应用路由返回空内容
          src:'http://localhost:3000/xxx'
         },
        el:myRef.current,
      });
 <WujieReact
        attrs={{
          // 主应用路由返回空内容
          src:'http://localhost:3000/xxx'
         }}
/>

@pandaOreo
Copy link

pandaOreo commented Feb 1, 2024 via email

@AttackXiaoJinJin
Copy link
Contributor

运行主应用的 js 资源污染了 iframe 沙箱

具体是哪些污染呢,可以具体列下吗?我能想到的是:

  • 主应用js调用window.location跳转,导致沙箱被替换
  • 主应用js向window塞了全局对象影响了wujie的全局对象

@AttackXiaoJinJin
Copy link
Contributor

我试了以下几种方法都不行(痛苦面具):

  • 同时设置 src 和 srcdoc,在 srcdoc 里写 script 移除 srcdoc,可以不让 src 加载资源,但 iframe.contentWindow.location 是 srcdoc,要想修改 location,只能显式写iframe.src=主应用host(还是得加载主应用 host)
  • 在上面的基础上,使用 history.replaceState 修改 url 以达到修改 location 的目的,但该 api 只允许同源修改 path,不能修改 host(说白了还是得先加载主应用 host)
  • 创建 iframe 时,使用 iframe 的 sandbox 属性禁止脚本加载,以达到禁止加载主应用的 js ,在 iframe onload 时移除 sandbox 属性,以方便 wujie js 执行,但即使移除了 sandbox,iframe 仍然延续 sandbox 属性的,除非再一次执行 iframe.src(还是得加载主应用 host)
  • 通过 js 劫持 iframe 的网络请求,但 gpt 说 iframe.src 请求是浏览器级别发起的,js 没有权限

@towertop
Copy link
Contributor

我不是太理解这个问题,也没在Chrome上复现和观测到。但我加上了方案1的设置,{attrs: { src: "/iframe.html"}},iframe是在宿主应用写的一个静态的空白文件,观测子应用加载后iframe加载了这个html文件,之后子应用内的 history.pushState 等都可以正常使用,但回退按钮异常。

不太确定 aliyun/alibabacloud-alfa#99 中提到的“当子应用跳转路由后点击浏览器后退发现无效“这是什么现象。我现在在保活模式下,路由之后回退按钮下的历史会有很多条,并且回退1次正常,但是前进按钮下会多出很多记录,并且回退2次时整个应该直接被chrome退到了newtab(比如默认搜索)页面,偶尔也见到回退按钮直接变灰禁用。

我认为跟 Chrome 等浏览器厂商实现的回退过程中一些启发式跳过的算法有关,也许是子应用的sync同步引发的,有待进一步调查:whatwg/html#7832

@pandaOreo
Copy link

pandaOreo commented Apr 24, 2024 via email

@Flame-Y
Copy link

Flame-Y commented Aug 26, 2024

使用vue2在子应用添加 :degrade="true" 似乎就能正常加载了,这是什么原理🤔

@pandaOreo
Copy link

pandaOreo commented Aug 26, 2024 via email

@robertpanvip
Copy link

iframe.src = 'about:blank'
这种方式我看可以访问啊 这种方式有啥问题吗

@pandaOreo
Copy link

pandaOreo commented Dec 26, 2024 via email

@yiludege
Copy link
Collaborator

yiludege commented Dec 26, 2024

iframe.src = 'about:blank' 这种方式我看可以访问啊 这种方式有啥问题吗

问题大了,子应用路由无法跳转,因为window.location.origin 是'about:blank',路由跳转的时候会出现跨域问题

@robertpanvip
Copy link

robertpanvip commented Dec 26, 2024

iframe.src = 'about:blank' 这种方式我看可以访问啊 这种方式有啥问题吗

问题大了,子应用路由无法跳转,因为window.location.origin 是'about:blank',路由跳转的时候会出现跨域问题

那伪造一个当前同源的独特的url 用注册 Service Worker 并拦截请求返回一个空白内容可行吗

self.addEventListener('fetch', (event) => {
  if (event.request.url.endsWith('/virtual-path')) {
    event.respondWith(
      new Response('<h1>This is a virtual page</h1>', {
        headers: { 'Content-Type': 'text/html' },
      })
    );
  }
});

@robertpanvip
Copy link

大佬 感觉 Service Worker 拦截请求 这个方案可行啊

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
good question Good for newcomers
Projects
None yet
Development

No branches or pull requests