You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

通过window.postMessage的history.push跳转后浏览器返回按钮异常问题

问题背景
  • 网站包含React页面和Django模板渲染的iframe页面,通过window.postMessage实现iframe到React页面的跳转
  • Django侧发送消息代码:
window.parent.postMessage(JSON.stringify(postData), "https://" + main_domain);
  • React侧监听消息并跳转的代码:
let { name } = useParams();

useEffect(() => {
  function handleMessage(e, originName) {
    if (originName === "from_iframe1") {
      props.history.push("next_url1");
    } else if (originName === "from_iframe2") {
      props.history.push("next_url2");
    }
  }
  
  if (name === "react_url1") {
    window.addEventListener(
      "message",
      e => handleMessage(e, "from_iframe1"),
      false
    );
  } else if (name === "react_url2") {
    window.addEventListener(
      "message",
      e => handleMessage(e, "from_iframe2"),
      false
    );
  } else {
    window.removeEventListener(
      "message",
      e => handleMessage(e, "others"),
      false
    );
  }
}, [name]);
  • 异常表现:首次跳转后需点击2次返回按钮回退,后续依次需要4次、6次;改用history.replaceState时首次跳转正常,但后续回退会跳过多个页面
核心原因

当前代码存在两个关键问题:

  1. 事件监听无法正确移除:每次name变化时注册的是匿名箭头函数,移除时用的是另一个新的匿名函数,两者引用不同,导致旧的监听一直存在,同一个message事件会被多次触发,每次触发都执行一次history.push,历史栈中堆积大量重复记录
  2. 监听逻辑冗余:针对不同name注册不同的监听函数,导致监听函数数量随页面切换不断增加
可行解决方案

方案1:修复事件监听的添加与移除逻辑

将监听函数提取为独立引用,确保添加和移除的是同一个函数,同时统一监听逻辑:

let { name } = useParams();

useEffect(() => {
  // 定义统一的监听函数,内部判断消息来源和当前页面
  function handleMessage(e) {
    // 必须验证消息来源合法性,防止跨域恶意攻击
    if (e.origin !== "https://" + main_domain) return;
    
    const data = JSON.parse(e.data);
    const originName = data.originName; // 假设postData包含标识来源的字段
    
    if (name === "react_url1" && originName === "from_iframe1") {
      props.history.push("next_url1");
    } else if (name === "react_url2" && originName === "from_iframe2") {
      props.history.push("next_url2");
    }
  }

  window.addEventListener("message", handleMessage, false);
  
  // 组件卸载或name变化时自动移除监听
  return () => {
    window.removeEventListener("message", handleMessage, false);
  };
}, [name, props.history, main_domain]);

方案2:限制跳转操作的执行频率

用标记位防止重复触发跳转,避免同一消息导致多次history.push:

let { name } = useParams();
const [isNavigating, setIsNavigating] = useState(false);

useEffect(() => {
  function handleMessage(e) {
    if (e.origin !== "https://" + main_domain) return;
    if (isNavigating) return; // 正在跳转时忽略后续重复消息
    
    const data = JSON.parse(e.data);
    const originName = data.originName;
    
    setIsNavigating(true);
    if (originName === "from_iframe1") {
      props.history.push("next_url1");
    } else if (originName === "from_iframe2") {
      props.history.push("next_url2");
    }
    // 延迟重置标记,避免短时间内重复跳转
    setTimeout(() => setIsNavigating(false), 500);
  }

  window.addEventListener("message", handleMessage, false);
  return () => window.removeEventListener("message", handleMessage, false);
}, [name, props.history, main_domain, isNavigating]);

方案3:优化history跳转逻辑

跳转前检查当前路径是否已经是目标路径,避免重复添加历史记录:

function handleMessage(e) {
  if (e.origin !== "https://" + main_domain) return;
  const data = JSON.parse(e.data);
  const originName = data.originName;
  let targetUrl = "";
  
  if (originName === "from_iframe1") {
    targetUrl = "next_url1";
  } else if (originName === "from_iframe2") {
    targetUrl = "next_url2";
  }
  
  // 仅当当前路径不等于目标路径时才执行跳转
  if (window.location.pathname !== `/${targetUrl}`) {
    props.history.push(targetUrl);
  }
}

方案4:检查iframe侧的消息发送逻辑

确认Django侧的postMessage是否被多次调用(比如按钮绑定了多次点击事件、页面渲染时重复执行),导致React侧收到多条重复消息。可以添加标记位确保只发送一次消息:

// Django模板中添加防重复标记
let hasSentJumpMessage = false;
function sendJumpMessage() {
  if (hasSentJumpMessage) return;
  hasSentJumpMessage = true;
  window.parent.postMessage(JSON.stringify(postData), "https://" + main_domain);
}
// 按钮点击时调用sendJumpMessage,而非直接执行postMessage

内容的提问来源于stack exchange,提问作者Anushka Chauhan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 16:33:09