指南 / Web

Chrome 里一切正常。然后 Safari 走进了房间。

功能能在 Chrome 运行,不等于它真的能在 Web 上运行。浏览器引擎、权限、布局规则和移动端行为,随时可能把“已经完成”的功能变成第二个调试项目。

发布 · 更新 · 9 MIN

功能写完了。

Chrome 正常,Android 正常,本地测试也正常。我连续刷新了几次,按钮一个个点过去,甚至产生了开发者最危险的情绪之一:自信。

然后我打开 Safari。

按钮在。页面也没崩。点一下,什么都没发生。没有报错弹窗,没有白屏,Safari 只是非常平静地表示:今天不想执行这个功能。

这就是跨浏览器开发。

“Chrome 能用”不等于“Web 能用”

浏览器并不只是把 HTML 画出来的窗口。背后还有浏览器引擎。Chrome 与大多数 Chromium 浏览器主要使用 Blink,Firefox 使用 Gecko,Safari 使用 WebKit。它们都实现 Web 标准,但实现进度、安全策略、平台限制和边缘行为并不总是一致。

普通段落通常不会让这些差异冒头。可一旦碰到文件、剪贴板、PWA、触摸、媒体、存储、权限或比较新的 CSS,浏览器就开始各自发表意见。

标准相同,运行条件却可能不同

例如复制到剪贴板,看起来只需要:

await navigator.clipboard.writeText(text);

现实里的条件可能还有 HTTPS、页面处于前台、浏览器允许该 API、刚刚发生过用户点击,以及当前版本没有额外限制。

所以比“判断是不是 Safari”更稳妥的办法,通常是判断能力:

if (navigator.clipboard?.writeText) {
  await navigator.clipboard.writeText(text);
} else {
  showManualCopyFallback();
}

浏览器名字告诉你它是谁,能力检测告诉你它现在能干什么。代码真正关心的是后者。

桌面 DevTools 的手机尺寸,不是真手机

把 Chrome DevTools 拉成 iPhone 宽度,只能模拟一部分视口。真实手机还有触摸事件、软键盘、Safe Area、浏览器顶部和底部工具栏、后台挂起、文件选择器、内存限制以及不同的权限交互。

经典例子是 100vh。移动浏览器的工具栏会出现、消失,可用高度因此变化。后来 CSS 出现了 dvh、svh、lvh,本质上是在承认一件很 Web 的事实:连“屏幕有多高”都需要好几种答案。

iPhone 上换个浏览器图标,也不一定换掉所有底层规则

在 iOS 上,浏览器长期受到 WebKit 平台规则的强约束。因此“iPhone 上的 Chrome”和 Windows 上的 Chrome,并不意味着底层能力完全相同。调试 PWA、文件处理、Web Share、媒体播放、存储和后台行为时,这一点尤其重要。

所以 Android Chrome 正常、iPhone 失败时,不要先怀疑 JavaScript 被附身。先看平台和浏览器能力差异。

Safari 也不一定是坏人

开发者吐槽 Safari 很常见,有些吐槽也确实有依据。但另一些时候,它只是把你代码里原本隐藏的假设翻了出来。比如你默认所有浏览器都会自动播放媒体,默认剪贴板永远可用,默认本地存储不会受隐私策略影响。

比“为什么 Safari 又坏了”更有用的问题是:我的代码假设了什么浏览器行为?这个问题通常离真正的 Bug 更近。

我现在最低限度怎么测

  1. 桌面 Chromium。
  2. 真实 Android 浏览器。
  3. Safari 或真实 iPhone。
  4. 窄屏和横屏。
  5. 键盘、触摸与软键盘弹出后的布局。
  6. 权限允许与拒绝两种路径。
  7. 无旧缓存、无登录状态的新会话。

尤其是权限。开发者非常擅长点击巨大的“允许”,用户则很擅长点“拒绝”。后者也必须是合法路径。

真正的目标不是让所有浏览器一模一样

跨浏览器支持并不意味着每个像素、每个功能都完全一致。真正重要的是核心任务在能力不足时还能优雅退化:高级分享不可用,就提供复制链接;自动剪贴板不可用,就显示手动复制;PWA 不能安装,就继续当正常网站使用。

有降级方案的功能仍然是功能。

只在开发者自己电脑上的那个 Chrome 标签页里工作,则更接近 Demo。

所以下次 Chrome 告诉你“全部完成”时,可以庆祝一下。

但先别开香槟。

Safari 还没进房间。