作者:damyxu,腾讯 PCG 前端开发工程师
前端开发中我们对 iframe 已经非常熟悉了,那么 iframe 的作用是什么?可以归纳如下:
在一个 web 应用中可以 独立 的运行另一个 web 应用
这个概念已经和 微前端 不谋而合,相对于目前配置复杂、高适配成本的微前端方案来说,采用 iframe 方案具有一些显著的 优点 :
但是开发者又厌恶使用 iframe ,因为 缺点 也非常明显:
能否打造一个完美的 iframe ,保留所有的优点的同时,解决掉所有的缺点呢?
无界微前端框架通过继承 iframe 的优点,解决 iframe 的缺点,打造一个接近完美的 iframe 方案
来看无界如何一步一步的解决 iframe 的问题,假设我们有A应用,想要加载B应用:
在应用A中构造一个 shadow 和 iframe ,然后将应用B的 html 写入 shadow 中, js 运行在 iframe 中, 注意 iframe 的 url , iframe 保持和主应用同域但是保留子应用的路径信息,这样子应用的 js 可以运行在 iframe 的 location 和 history 中保持路由正确。
在 iframe 中拦截 document 对象,统一将 dom 指向 shadowRoot ,此时比如新建元素、弹窗或者冒泡组件就可以正常约束在 shadowRoot 内部。
接下来的三步分别解决 iframe 的三个缺点:
将这套机制封装进 wujie 框架:
我们可以发现:
由于子应用完全独立的运行在 iframe 内,路由依赖 iframe 的 location 和 history ,我们还可以在一张页面上同时激活多个子应用,由于 iframe 和主应用处于同一个 top-level browsing context ,因此浏览器前进、后退都可以作用到到子应用:
通过以上方法,无界方案可以做到:
如果主应用是 vue 框架:
安装
npm i @tencent/wujie-vue -S`
引入
mport WujieVue from "@tencent/wujie-vue";
Vue.use(WujieVue);
使用
<WujieVue
width="100%"
height="100%"
name="xxx"
url="xxx"
:sync="true"
:fetch="fetch"
:props="props"
@xxx="handleXXX"
></WujieVue>
其他框架也会在近期上线
无界的适配成本非常低
对于主应用无需做任何改造
对于子应用:
webpack
if (window.__POWERED_BY_WUJIE__) {
let instance;
window.__WUJIE_MOUNT = () => {
instance = new Vue({ router, render: (h) => h(App) }).$mount("#app");
};
window.__WUJIE_UNMOUNT = () => {
instance.$destroy();
};
} else {
new Vue({ router, render: (h) => h(App) }).$mount("#app");
}
子应用运行在一个和主应用同域的 iframe 中,设置 src 为替换了主域名 host 的子应用 url ,子应用路由只取 location 的 pathname 和 hash
但是一旦设置 src 后, iframe 由于同域,会加载主应用的 html 、 js ,所以必须在 iframe 实例化完成并且还没有加载完 html 时中断加载,防止污染子应用
此时可以采用轮询监听 document.readyState 状态来及时中断,对于一些浏览器比如 safari 状态不准确,可以在 wujie 主动抛错来防止有主应用的 js 运行
子应用的代码 code 在 iframe 内部访问 window , document 、 location 都被劫持到相应的 proxy ,并且还会注入 $wujie 对象供子应用调用
const script = `(function(window, self, global, document, location, $wujie) {
${code}\n
}).bind(window.__WUJIE.proxy)(
window.__WUJIE.proxy,
window.__WUJIE.proxy,
window.__WUJIE.proxy,
window.__WUJIE.proxy.document,
window.__WUJIE.proxy.location,
window.__WUJIE.provide
);`;
iframe 内部的副作用处理在初始化 iframe 时进行,主要分为如下几部
/**
* 1、location劫持后的数据修改回来,防止跨域错误
* 2、同步路由到主应用
*/
patchIframeHistory(iframeWindow, appPublicPath, mainPublicPath);
/**
* 对window.addEventListener进行劫持,比如resize事件必须是监听主应用的
*/
patchIframeEvents(iframeWindow);
/**
* 注入私有变量
*/
patchIframeVariable(iframeWindow, appPublicPath);
/**
* 将有DOM副作用的统一在此修改,比如mutationObserver必须调用主应用的
*/
patchIframeDomEffect(iframeWindow);
/**
* 子应用前进后退,同步路由到主应用
*/
syncIframeUrlToWindow(iframeWindow);
ShadowRoot 内部的副作用必须进行处理,主要处理的就是 shadowRoot 的 head 和 body
shadowRoot.head.appendChild = getOverwrittenAppendChildOrInsertBefore({
rawDOMAppendOrInsertBefore: rawHeadAppendChild
}) as typeof rawHeadAppendChild
shadowRoot.head.insertBefore = getOverwrittenAppendChildOrInsertBefore({
rawDOMAppendOrInsertBefore: rawHeadInsertBefore as any
}) as typeof rawHeadInsertBefore
shadowRoot.body.appendChild = getOverwrittenAppendChildOrInsertBefore({
rawDOMAppendOrInsertBefore: rawBodyAppendChild
}) as typeof rawBodyAppendChild
shadowRoot.body.insertBefore = getOverwrittenAppendChildOrInsertBefore({
rawDOMAppendOrInsertBefore: rawBodyInsertBefore as any
}) as typeof rawBodyInsertBefore
getOverwrittenAppendChildOrInsertBefore 主要是处理四种类型标签:
由于 js 在 iframe 运行需要和 shadowRoot ,包括元素创建、事件绑定等等,将 iframe 的 document 进行劫持:
将 iframe 的 location 进行劫持:
通过上面原理以及细节的阐述,我们可以得出无界微前端框架的几点优势:
相应的也有所不足:
原文 https://zhuanlan.zhihu.com/p/442815952
如何兼容支持iframe框架的背景透明。通过使用allowtransparency和background-color来设置iframe框架的透明效果。
表单提交是同步的,会阻塞页面,可以将表单提交到一个iframe中,父页面就不会阻塞了。需要设置form的target属性等于iframe的name属性。服务器端只是单纯对表单提交的响应,可以返回一段script脚本作为http响应流,执行javascript。
页面中有个iframe元素,iframe元素的src是iframe1.html,怎么在demo1.html页面中操作iframe1.html页面,js先找到iframe元素(比如命名为:oIframe),那么oIframe.contentWindow就是iframe1.html这个页面的window
整体思路:在iframe所在当前页面地址url后拼接 #+targetUrl,iframe标签内a标签跳转,父页面添加如下方法,iframe标签内调用父页面js方法用 window.parent.jsfun() 调用。
当一个页面使用了iframe作为嵌套时,如何想要将父页面的数据传给iframe子页面,那iframe所指向的呢个子页面是怎么获取呢,又或者子页面的数据要给父页面使用,那么父页面又如何获取子页面的数据呢?
最近接手了一个古旧的项目,跟客户端、服务器端一起调一个支付相关的app内嵌H5页面,这个页面有两部分组成,主页面A加上一个最终支付页面B,B页面是通过iframe嵌入到A页面中的,A、B两个页面之间的交互采用postMessage+hashChange
iframe在web应用刀耕火种的开发年代是非常常见的,现在基于Node构建的前端应用嵌入iframe的场景越来越少了,但是在大型的web应用中也会经常遇见利用iframe嵌入多个前端应用于一套前端系统中,方便用户在一个系统中去进行业务操作
通过document.addEventListener(\\\"scroll\\\",function(){})对页面滚动监听事件进行监听,但ios下$(document).scrollTop()值始终为0,对页面监听无效。
在最近的项目里面,用到了不少关于iframe父子传参通信的相关操作,记录一下,虽然很简单,但是确实十分有用的;iframe通信可以分为2种,跨域和非跨域两种.分别说明;
在AAA.com域名下的index.htm页面中内嵌了BBB.com域名下的一个页面index.htm,正常情况下iframe内部的index.htm页面是无法访问父页面index.htm中的任何dom对象或者js函数的,因为跨域,但我们经常又需要做一些参数回传的事情怎么办呢?以上的这种实现方式就很好的解决了这个问题
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!