为什么 Safari 显示“Link Not Found”(而 Chrome 没有)

发布: (2026年4月22日 GMT+8 09:35)
5 分钟阅读
原文: Dev.to

Source: Dev.to

请提供您希望翻译的具体文本内容,我将为您翻译成简体中文。

Problem

我们构建的 URL 缩短服务在 Safari(iOS 和 macOS)上显示 “Link Not Found” 错误,而 Chrome、Firefox 和 Edge 则正常工作。iPhone 用户点击短链接时,有时会在目标页面加载之前短暂闪现错误页面。此问题表现为间歇性,难以复现,并且在 Chrome 上从未出现。

调查

我们添加了轻量级调试仪器:一个 useRef 数组记录每个函数调用的时间戳,并持久化到 localStorage,使日志在导航后仍然保留。

示例调试输出来自用户设备:

[0] { event: "handleRedirect called", timestamp: 1709683200001 }
[1] { event: "API success, redirecting", timestamp: 1709683200250 }
[2] { event: "handleRedirect called", timestamp: 1709683200252 }
[3] { event: "API error: request cancelled", timestamp: 1709683200260 }

条目 [2] 显示 handleRedirect 被调用了 两次,第二次调用在第一次成功重定向后仅 2 ms。

组件的行为

  1. 挂载useEffect 调用了 handleRedirect()
  2. handleRedirect() 通过 API 解析目标 URL
  3. 成功后,safeRedirect(targetUrl) 设置 window.location.href
  4. 导航后,状态被更新(markVisitedInSession),将 hasVisitedInSessionfalse 改为 true
  5. handleRedirectuseCallback 包裹,hasVisitedInSession 作为依赖项

当状态改变时,React 创建了一个 新的 handleRedirect 回调引用。useEffect 检测到依赖项变化并重新运行 effect,再次调用 handleRedirect——即使页面已经在导航中。

根本原因:Safari 的导航行为

  • Chrome:设置 window.location.href 会立即停止当前页面的 JavaScript 执行。第二个 handleRedirect 永远不会运行。
  • Safari:JavaScript 在导航进行期间仍会继续运行。第二次调用被触发,启动 API 请求,但由于页面正处于导航中,该请求被取消。catch 块渲染错误页面,产生“未找到链接”的闪现。

修复

我们引入了一个使用 useRef(而不是 useState)的 guard,以确保重定向逻辑只运行一次。

// Guard to prevent double redirects
const hasRedirected = useRef(false);

const handleRedirect = useCallback(async (pwd?: string) => {
  // If we already redirected and this isn’t a password retry, bail out
  if (hasRedirected.current && !pwd) return;

  // …resolve URL, handle previews, etc…

  // Mark that we have redirected before navigating
  hasRedirected.current = true;
  safeRedirect(targetUrl, '/');
}, [/* other deps */]);

为什么 useRef 有效

  • No re‑render: Updating a ref does not trigger a component render, so the callback identity stays the same.
  • Persistence across renders: The ref value survives across renders, silently blocking subsequent calls.

If we had used useState for the guard, setting it to true would cause another render, potentially creating a new callback identity and re‑triggering the effect—a loop we wanted to avoid.

后续

  • 已在 Safari、Chrome 和 Firefox 上验证了修复。
  • 移除了调试仪器(localStorage 日志记录和调试面板)。
  • 保留了 useRef 保护作为针对该 Safari 特定行为的三行防御。

要点

  • 浏览器导航行为不同:Chrome 在 window.location.href 时会暂停 JavaScript;Safari 则不会。这不是任一浏览器的 bug——规范并未规定何时必须停止执行。
  • React Hook 可能产生隐藏循环:当 useCallbackuseEffect 的依赖包含在回调内部更新的状态时,可能导致不可见的重新执行,尤其在导航介入时。
  • 在需要时对生产环境进行监控:当本地无法重现 bug 时,可通过标志位发布最小化的监控代码,让真实设备揭示真相。
  • 倾向使用 useRef 保存非渲染状态:当需要可变状态且不应触发渲染(例如“已重定向”标记)时,useRef 是合适的工具。

你是否曾被 Safari 特有的 JavaScript 行为坑到?在评论区分享你的故事吧。

jo4.io 构建 — 一个在所有浏览器(包括 Safari)上都能使用的 URL 缩短服务。

0 浏览
Back to Blog

相关文章

阅读更多 »