来啦,记录一下使用iframe遇到的一些问题
iframe的优势、缺点、限制
iframe的原理
iframe(Inline Frame) 是一种在网页中嵌套其他HTML项目的技术,可以实现多系统的嵌入,开发者可以在一个系统页面内完整地嵌入另一个页面的内容,这在一定程度上可以提高开发效率和提供了一种解决跨项目跨部门的方式。
iframe的使用
iframe的使用比较简单,用iframe标签即可实现引入,下方代码如是
<iframe
id="myIframe"
:src="TARGET_URL"
frameborder="0"
class="iframe"
@load="onLoad"
/>
- src: 是所嵌入页面(指定要加载)的url地址
- id: 为每个iframe指定id属性,标记唯一性,也可通过id获取iframe对象
- class: iframe设置的样式,通常要设置width,height
- load:iframe初加载之后执行的函数,可以在onLoad函数内获取id为 myIframe的iframe对象,可以选择性的进行数据传输
iframe怎么进行数据传输(四种方式)
- URL参数
直接通过url进行传参是最简单的方式,但是不适用于传递敏感信息,比如token之类的。
实现方式
//父级页面进行传递
<iframe id="myIframe" src="example.com?data=test parent"></iframe>
//iframe页面进行接收
var params = new URLSearchParams(window.location.search);
console.log(params.get('data')); // 'test parent'
- window对象
同源的情况下也可以通过window对象直接进行传递
// 父页面设置数据
let iframe = document.getElementById('myIframe');
iframe.contentWindow.someData = 'test parent';
// iframe访问数据
console.log(window.someData); // 'test parent'
- Cookie、LocalStorage
在同源的情况下也可以通过共享cookie或者存储LocalStorage来进行数据的传输,记得要同源
//cookie的获取和存储都要同源或者后端在set-cookie时设置domain两个系统顶级域名
// 主页面:aa.test.com
// 子页面:bb.test.com
//这两个页面进行cookie共享时可以在主页面设置domain
//一般由后台设置,前端的window.domain 已失效
//LocalStorage
// 父页面进行数据存储,在iframe加载后
const onLoad = async () => {
localStorage.setItem('TestData', data);
}
//子页面进行数据的获取
window.addEventListener('load', function() {
// 获取localStorage中的数据
let data = localStorage.getItem('TestData');
}, false);
- postMessage方法
postMessage是最安全最稳定的一种传输方式,可以不用考虑同源,就可以直接进行数据的传递。不仅能传递字符串,还能传递对象。这使得它非常灵活,可以用来传递复杂的数据结构
//父页面进行数据的传递
const onLoad = async () => {
// 获取iframe的window对象
const iframeWindow = (document.getElementById('myIframe') as any).contentWindow
await iframeWindow.postMessage(
{
status: 'ok',
params: {
iframe: true,
token: getToken(), //可以传递参数过去
},
},
'*',
)
}
//子页面进行数据的监听接收
window.addEventListener('message', async (event) => {
if (event.data.status === 'ok') {
const params = event.data.params
// console.log(params, '父页面传递的参数')
if (params.token) {
setToken(params.token) //自行存储token
}
}
})
iframe的一些限制
iframe的使用虽然说比较简单,但是还是有不少限制的。
- url不同步,浏览器刷新iframe的url状态丢失
- dom结构不共享,属于嵌套逻辑,子系统被主系统包围
- 内存变量不共享,不同源的情况下不方便进行数据传输
系统内嵌iframe链接导致主系统路由不发生变化的问题
上面也提到iframe也有一些限制,比如遇到的最大限制是url不同步,浏览器刷新iframe的url状态丢失。
比如下面图片,url是系统url,但是怎么切换路由都不是事iframe子页面相对应的路由,是父页面的/home路由,因为我是在/home内嵌入的,这也就导致两个问题
1. 切换子页面的菜单,父页面并不会随之更新
2. 刷新页面,路由状态消失,刷新后相当于重新加载子页面
这里是怎么解决的呢
- 分析下要解决的重点,也就是地址内的路由要跟随子页面的路由去更新
后面了解有 window.history.replaceState方法,这个主要是改变页面地址url,但是不进行页面的刷新,可以说这个很好的解决了我的问题。具体解决方式如下。
//子页面每次切换路由后,都会向父页面传递最新的路由路径过去
// 通过window.parent.postMessage的方法,定义一个routeUpdate的type来进行区分
router.afterEach(() => {
NProgress.done()
const hash = window.location.hash.split('#')[1]
window.parent.postMessage({ type: 'routeUpdate', newURL: hash }, VITE_API_IFRAME_URL)
})
//父页面在APP页面进行监听并接收,同样也是监听message方法
// 接收子系统传过来的数据,通过事件监听进行处理
window.addEventListener('message', ({ data }) => {
/* iframe路由更新,嵌入子系统的话不会随着子系统的路由进行更新,这里采用 window.history.replaceState() 进行更新,但不刷新页面 */
if (data.type === 'routeUpdate') {
// 主要代码是这里,这样可以保证切换子页面时,父级页面路由也是更新的
window.history.replaceState({}, '', data.newURL)
}
})
但是解决切换路由跟随更新后,还有一个问题,
- 确保页面刷新后,路由仍是之前的地址path,且子页面能及时加载当前路由页面。
解决如下
//取部分路由守卫放这里
router.beforeEach(async (to: any, from: any, next: any) => {
// 判断该用户是否登录
if (getToken()) {
if (to.path === '/login') {
// 核心在这里!!!如果已经登录,并准备进入 Login 页面,则重定向到主页,把pathname给redirect
window.location.pathname === '/' ? next(`/home`) : next(`/home?redirect=${window.location.pathname}`)
}
else {
next()
}
}
else {
// 核心在这里!!! 确保登录失效的时候把之前设置的replaceState置为{},地三个参数记得加/,否则失效
window.history.replaceState({}, '', '/')
next()
}
})
//在iframe内,动态改变路径
//截取路径后的redirect内容,在iframe标签内动态添加redirect重定向,在子页面的路由守卫进行监听就可以了
const redirect = ref('')
const location = window.location.href as any
if (location.split('redirect=/')[1]) {
redirect.value = loc.split('redirect=/')[1]
}
<iframe
id="myIframe"
:src="TARGET_URL + redirect"
frameborder="0"
class="iframe"
@load="onLoad"
/>
cookie共享在iframe的应用
cookie在iframe的使用是一定要求同源的!!
不同源的话也可以,但是要求父子系统的访问域名的一级域名一致,然后设置domain=‘一级域名’,即可,这样也可以实现的cookie的共享(存和取)
主页面:aa.test.com
子页面:bb.test.com
这两个页面进行cookie共享时可以在主页面设置domain
一般由后台设置,前端的window.domain 已失效
好啦,上面分享时最近使用iframe的一些想法,涉及的较为全面,可进行补充