- 作者:老汪软件技巧
- 发表时间:2024-08-30 11:01
- 浏览量:
iframe遵守同源策略,只有父页面与嵌套页面来自同一个域名,两者才能通信。所以首先需要进行同域代理。
1、怎么实现同域代理
server {
listen 80;
server_name yourdomain.com;
location /proxy/ {
proxy_pass http://targetdomain.com/;
proxy_set_header Host targetdomain.com;
add_header X-Frame-Options ALLOWALL;
}
}
2、如何不改动子页面代码的情况下监听iframe嵌套页面的路由变化2.1、代理iframe的 History 对象(对跨域不适用)
async mounted() {
this.watchIframeUrl();
},
methods: {
/**
* 监听iframe的URL变化
* 该方法通过劫持iframe窗口的历史记录API来实现
* 主要用于捕获pushState和replaceState的操作
*/
watchIframeUrl() {
// 获取iframe元素
const iframe = this.$refs.iframe;
// 获取iframe的窗口对象
const iframeWindow = iframe.contentWindow;
// 备份原始的pushState方法
const originalPushState = iframeWindow.history.pushState;
// 备份原始的replaceState方法
const originalReplaceState = iframeWindow.history.replaceState;
// 覆盖pushState方法,以监听URL的变化
iframeWindow.history.pushState = function (...args) {
// 调用原始的pushState方法,并传递所有参数
originalPushState.apply(iframeWindow.history, args);
// 打印pushState操作的参数和状态变化
console.log('iframe pushState: ', args, args[2]);
// 检查特定的URL,如果有匹配,则执行相关操作
if (
args[2] ===
'/eolink/router/api/message/5247eb64-011d-055a-fe8f-741cfa8fe78d'
) {
// 执行与特定URL相关的操作
console.log('push');
}
};
// 覆盖replaceState方法,以监听URL的变化
iframeWindow.history.replaceState = function (...args) {
// 调用原始的replaceState方法,并传递所有参数
originalReplaceState.apply(iframeWindow.history, args);
// 打印replaceState操作的参数和状态变化
console.log('iframe replaceState: ', args, args[2]);
// 检查特定的URL,如果有匹配,则执行相关操作
if (args[2] === '/eolink/router/api/group/list') {
// 执行与特定URL相关的操作
console.log('replace');
}
};
}
}
但同样的,这种方法在跨域情况下是无法实现的。
2.2、通过 MutationObserver 观察 iframe 的 URL 变化
const iframe = document.querySelector('iframe');
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === 'attributes' && mutation.attributeName === 'src') {
console.log('iframe src changed to: ', iframe.src);
}
});
});
observer.observe(iframe, { attributes: true });
这种方法的局限性是:如果子页面是单页应用程序(SPA) ,它的 src 可能不会变,所以这个方法无法捕捉内部的路由变化。
2.3、定时轮询 iframe 的 URL
data() {
retuern {
intervalId: null
}
}
mounted() {
const iframe = this.$refs.iframe;
const pollingInterval = 1000; // 设置轮询间隔时间(毫秒)
// 检查 iframe 的 URL
const checkIframeUrl = () => {
// 当前iframe的src
const currentUrl = iframe.contentWindow.location.href;
// 具体的逻辑
const baseUrl = `${window.location.origin}/ang_kai-db`;
if (
![
`${baseUrl}/user-info`,
`${baseUrl}/user-password`,
`${baseUrl}/user-manage`
].includes(currentUrl)
) {
this.setCss();
}
};
// 启动轮询
this.intervalId = setInterval(checkIframeUrl, pollingInterval);
}
destroyed() {
this.cleanup();
},
methods: {
cleanup() {
clearInterval(this.intervalId);
},
}
这个方法虽然简单直接,但有一定的性能开销,而且如果 iframe 中的页面存在跨域限制(如 SameSite 属性),你将无法访问 contentWindow 的属性,这种方法就无法使用。
3、iframe扩展3.1、contents()获取iframe嵌套页面的dom树进行模拟点击
这个方法在我的一个需求中使用到了。需求是这样的:iframe嵌套了子页面,第一,需要将子页面的顶部菜单栏进行隐藏;第二,需要将子页面顶部菜单栏右上角的一些功能移到父页面的右上角,如下图所示:
当你需要操作 iframe 中的元素时,先使用 contents() 获取 iframe 内部的文档对象,然后在这个文档对象上使用 jQuery 方法来操作其内容。例如:
const iframeContent = $('#myIframe').contents();
const iframeElement = iframeContent.find('#childElement');
iframeContent.find('#childElement').click() // 模拟点击
这里的 contents() 用于获取 iframe 的文档对象,然后在该文档对象内使用 find() 查找特定的元素。