[vue] 动态修改iframe的src属性导致路由历史记录异常的解决方案

1. 问题重现:一个看似简单的需求,怎么就“坏”了?

最近在做一个Vue的后台管理系统,里面有个需求,需要在主页面里嵌入一个内嵌的页面,也就是我们常说的iframe。这个iframe的内容不是固定的,需要根据用户点击不同的菜单或者按钮,动态地切换它里面显示的内容。听起来是不是挺简单的?我当时也是这么想的,不就是改一下iframe标签的src属性嘛,v-bind一下,一个响应式数据就搞定了。

于是我就写出了类似这样的代码:

<template>
  <div>
    <button @click="loadPageA">加载页面A</button>
    <button @click="loadPageB">加载页面B</button>
    <iframe ref="myIframe" :src="https://blog.csdn.net/iframeUrl" width="100%" height="600px"></iframe>
  </div>
</template>

<script>
export default {
  data() {
    return {
      iframeUrl: ''
    }
  },
  methods: {
    loadPageA() {
      this.iframeUrl = 'https://example.com/page-a';
    },
    loadPageB() {
      this.iframeUrl = 'https://example.com/page-b';
    }
  }
}
</script>

功能上线初期,一切正常。用户点按钮,iframe内容唰一下就变了,美滋滋。但没过多久,测试同事和用户就开始反馈一个诡异的问题:在切换了几次iframe内容后,点击浏览器的后退按钮,或者调用Vue Router的$router.go(-1),页面并没有回到切换iframe之前的状态,而是直接跳出了当前页面,或者行为完全不可预测。

这可就麻烦了。用户的使用路径被打乱了,体验非常差。我一开始还以为是Vue Router的配置问题,或者是组件生命周期哪里没处理好,排查了一圈,最后才发现“罪魁祸首”就是这个我亲手写的、看起来人畜无害的动态src绑定。

1.1 根源剖析:为什么动态修改src会“污染”历史记录?

要理解这个bug,我们得先抛开Vue,看看浏览器最基础的行为。当我们创建一个iframe并设置其src时,浏览器会向这个地址发起请求,加载页面。这里有一个关键细节:如果这个src指向的地址与父页面(也就是你的Vue应用)同源,那么这次加载行为,会被浏览器视为一次页面导航

什么叫“同源”?简单说,就是协议(http/https)、域名、端口号完全一致。比如你的Vue应用跑在https://admin.myapp.com,你的iframe加载的是https://admin.myapp.com/some-page,这就是同源。

浏览器有个机制,对于同源的iframe,每次通过修改iframe.src或者iframe.contentWindow.location.href来加载新页面,都会在浏览器全局的window.history对象中新增一条历史记录。注意,是全局的,不是你Vue应用内部路由的那个历史记录栈。

想象一下这个场景:

  1. 你进入Vue应用的 /home 页面。(历史记录:[home])
  2. 你点击按钮,把iframe的src从空改为/page-a。(历史记录变为:[home, /page-a])
  3. 你再点击按钮,把iframe的src改为/page-b。(历史记录变为:[home, /page-a, /page-b])

此时,你调用this.$router.go(-1),Vue Router期望的是在自己的路由栈里后退一步。但浏览器看到的历史记录栈顶是/page-b,它执行的是全局后退,于是iframe的内容可能变回了/page-a,而你的Vue路由可能根本没动,或者发生了更混乱的跳转。这就是路由“失控”和“混乱”的根本原因——两套历史记录系统(Vue Router的和浏览器全局的)打架了

2. 解决方案:location.replace 的妙用

知道了病因,开药方就有的放矢了。我们的核心目标很明确:在动态改变同源iframe内容时,阻止浏览器新增历史记录

这时候,location.replace()方法就该登场了。它和直接赋值srclocation.href最大的区别在于:replace()会用新的页面替换当前历史记录条目,而不是新增一条

把它应用到iframe里,就是操作iframe内部窗口的location对象:

this.$refs.myIframe.contentWindow.location.replace(newUrl);

这个方法会加载新的URL,但当前iframe在浏览器历史记录中的位置被新页面“覆盖”了。这样一来,无论你在同一个ifram

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值