WebView 里的一个链接或 window.open 并不会打开新标签页——它悄无声息地什么也不做。Android 默认 setSupportMultipleWindows(false),会忽略一切打开新窗口的请求;要让它生效,App 必须开启多窗口并实现 onCreateWindow 与 onCloseWindow,而多数 App 从未实现。iOS 上 WKWebView 的 createWebViewWithConfiguration 委托默认返回 nil,于是 target=_blank 被静默丢弃,除非 App 实现 WKUIDelegate;而开发者实现后,又常常新建第二个 WKWebView,其前进/后退历史与第一个完全脱节。宿主自有 runtime 提供统一窗口管理器:把弹窗映射为应用内标签页或一致的外部跳转,保持导航历史完整,绝不让链接静默失效。

Android:新窗口默认被忽略

Android 文档说得很直白:默认情况下系统会忽略打开新窗口的请求,无论是通过 JavaScript 还是 target 属性。要让 target=_blank 生效,App 必须调用 setSupportMultipleWindows(true) 并重写 onCreateWindow(以及用于清理的 onCloseWindow)。漏掉这个开关,onCreateWindow 永远不会被调用——点击没有导航、没有报错、什么都没有。在 App 生态里的实际结果,就是一长串「在新标签打开」的死链:在真实浏览器里能用,在 WebView 里凭空消失。

iOS:默认返回 nil 的委托

WKWebView 没有内置的标签页或窗口模型;当页面请求新的浏览上下文时,它通过 WKUIDelegate.createWebViewWithConfiguration 询问 App。默认实现返回 nil,意味着 target=_blank 与 window.open 被静默丢弃。开发者必须设置 uiDelegate 并实现该方法;常见的折中是把请求重新加载回同一个视图(丢失了本意的新标签语义),或新建一个全新的 WKWebView——它的前进/后退栈与原页面各自独立,用户被困住、找不到回去的路。

没有窗口管理器会失去什么

  • 死链且无反馈。 target=_blank 点击毫无反应,用户以为页面坏了。
  • 历史断裂。 第二个 WebView 拥有独立的前进/后退栈,把用户与来源页割裂。
  • 没有一致的外部链接策略。 有些链接应留在应用内,有些应交给系统浏览器;原生 WebView 没有规则。
  • 丢失会话上下文。 本应继承登录或购物车状态的弹窗,在独立上下文里从头冷启动。
  • 静默分化。 同一段 HTML 在 Safari 打开真正的标签,在嵌入视图里却什么也不做。

runtime 解法

宿主自有 runtime 在页面之下提供统一窗口管理器:把每一个新窗口请求映射到可预期的结果——共享导航历史与会话的应用内标签页,或清晰标注的、交给系统浏览器的外部跳转——并且绝不一声不响地吞掉链接。页面作者写标准的 target=_blank,runtime 按策略在两端一致地决定下一步发生什么。

维度WebView宿主自有 runtime
新窗口默认被忽略(Android)/ 委托返回 nil(iOS)映射为应用内标签或跳转
Android 开关需 setSupportMultipleWindows + onCreateWindowruntime 接管
iOS 路径需 WKUIDelegate.createWebViewWithConfigurationruntime 接管
导航历史第二个视图中断裂共享且完整
外部链接策略无,各 App 自行猜一致且可配置
静默死链常见绝不发生

浏览器把 target=_blank 当作一个承诺:一个新且可达的上下文。原生 WebView 把它当作一个可随意忽略的请求。runtime 恢复这一承诺——统一窗口管理器、完整历史、链接永远指向用户看得到的地方。

参考来源