Wenzi

Electron 中使用 webview 中打开外部链接跳转问题

蚊子前端博客
发布于 2026/05/13 13:43
Electron 中无法拦截 webview 中第三方页面的 window.open 的操作

最近遇到了一个比较棘手的问题,目前暂时还没有解决的方法,如果有同学曾经遇到过类似的问题,欢迎将解决方案分享出来。

我先简单描述一下问题:我目前使用的 Electron 版本是 34.x 版本,使用 webview 标签渲染外部链接。若该页面中使用了 window.open() 方法在新标签页中打开链接,在浏览器上是没有问题的,但在 electron 中是没有反应的。

1. 重写 window.open 方法 #

在咨询过 AI 后,是可以通过重写 window.open() 方法来实现的。有两种重写的方式,一种是在渲染层注入,另一种是在主进程注入。

渲染层注入:

webviewRef.current.addEventListener("dom-ready", () => {
  webviewRef.current.executeJavaScript(`
      // 强制覆盖,即使页面已经定义
      const originalOpen = window.open;
      window.open = function(url, name, features) {
          console.log('被拦截的 window.open:', url);
          // 主进程处理
          window.hivedoneApp.invoke("openUrl", { url });
          return null;
      };
      // 冻结这个属性,防止被再次修改
      Object.freeze(window.open);
  `);
});

主进程注入:

app.on("web-contents-created", (_, contents) => {
  contents
    .executeJavaScript(
      `
    // 保存原生 open
    // window._originalOpen = window.open; // 2

    // 永久重写
    window.open = function(url, target, features) {
      console.log('[重写] window.open:', url, Date.now());

      if (url && url.startsWith('http')) {
        // 主进程处理
        window.hivedoneApp.invoke("openUrl", { url });
      }

      // 阻止默认行为
      return null;
    };

    console.log('✅ 全局 window.open 重写成功!', Date.now());
  `,
    )
    .catch((err) => {});
});

这两种重写方式,都可以在 webview 中的页面生效。但若 webview 中加载的页面中还有一个 iframe,接着往下看。

2. webview 中嵌套 iframe 问题 #

但接下来就有两个问题一直没解决:

  1. 教程中都说 contents.setWindowOpenHandler() 可以拦截window.open()触发的行为,但实际测试发现,contents.setWindowOpenHandler() 并不能拦截 window.open() 触发的行为;目前还没找到原因;不过用重写 window.open() 方法倒也是可以解决。
  2. 若 webview 中加载页面中还有一个 iframe,iframe 中的 window.open() 是不会被重写的,若 iframe 中有 window.open() 触发的行为,直接就是无反应,同样的,iframe 中的 window.open() 方法也不会被 contents.setWindowOpenHandler() 拦截。

目前问题就卡住了,暂时还没有找到解决方法。

3. 更换成 WebContentView #

其实倒也是可以把 webview 替换成 WebContentView。但 WebContentView 是属于主进程创建的,层级太高,会遮挡住其他元素,导致其他元素无法正常显示。若页面中有一些弹窗、提示框等元素,就会有显示问题。

为了让这些弹窗能够正常显示,我可能还得要监控弹窗的显示和隐藏事件,然后再决定是隐藏还是显示 WebContentView。处理起来,也相当麻烦。我目前的方案是,监控页面元素的变动,然后判断网页中是否有弹窗、提示框等元素,若有,就隐藏 WebContentView,若无,就显示 WebContentView。

const observer = new MutationObserver(() => {
  const modals = Array.from(document.querySelectorAll(".ant-modal"));
  const popovers = Array.from(document.querySelectorAll(".ant-popover"));

  let isExisted =
    modals.filter((modal) => {
      return !modal.classList.contains("ant-modal-hidden") && !modal.classList.contains("ant-zoom-leave-active");
    }).length > 0;

  isExisted =
    isExisted ||
    popovers.filter((popover) => {
      return !popover.classList.contains("ant-popover-hidden");
    }).length > 0;

  // 若有弹窗、提示框等元素,就隐藏 WebContentView
  updateModalVisible(isExisted);
});
observer.observe(document.body, { childList: true, subtree: true, attributes: true });

4. 总结 #

用 webview 标签 和 WebContentView 两种方式都各有优劣,只能说更看重哪种交互方式,进行取舍。

当然,或许有什么更好的解决方案,欢迎分享出来。

标签:electron
阅读(1)
Simple Empty
No data