博客

WKWebView 是什么,它和 Safari 有什么不同

原理与选择 · · 约 8 分钟

WKWebView 是 Apple 的 WebKit 框架里用来显示网页内容的视图,跑的是 Safari 同款的 WebKit 引擎。它缺的是引擎外面那层浏览器,没有标签页、扩展、书签和 Safari 的设置,存储、用户代理和检查器都由应用自己决定。macOS 上的 Pake 应用就是一个原生窗口里套着一个 WKWebView,所以这些选择直接决定你的网站在里面怎么表现。

它是什么,替代了什么

Apple 对 WKWebView 的描述是一个显示交互式网页内容的对象,比如用于应用内浏览器,它属于 WebKit 框架,从 macOS 10.10 开始可用。应用用 WKWebViewConfiguration 创建它,Apple 说视图只在创建时读取这些设置,之后不能再改。

更早的视图是 iOS 上的 UIWebView,Apple 把它标为从 iOS 12 起弃用,并要求改用 WKWebView。macOS 上旧的 WebKit 类也已弃用,Apple 的 WebFrame 页面标注的是 macOS 10.3 到 10.14。实际意思是,现在 Mac 应用要显示网站,用的就是 WKWebView,Tauri 用来绘制网页的 wry 库在 macOS 上也是这样,它的 README 写的是 WebKit,源码里创建的是 WKWebViewConfiguration。

WKWebView、Safari 和引擎

引擎是同一个。Tauri 网页视图说明说 macOS 用的是 10.10 起预装的网页视图,由系统更新,不再收到更新的 macOS 版本也就不再收到 WebKit 更新。它的表格把 macOS 版本和 Safari 版本对应起来,比如 macOS Sonoma 14.0 测试版对应 WebKit 616.1.14.11.11 和 Safari 17.0。所以在你这个 macOS 版本上的 Safari 里表现正常的页面,在同一版本的 WKWebView 里也应该正常,更新方式也一样,都是升级 macOS。

不跟着过来的是浏览器本身。Apple 的网页 App 支持页面里,网页应用的工具栏有你已安装的 Safari 扩展的按钮,那是 Safari 的功能,不是 WKWebView 自带的。放到 Pake 里,文档里能看到三个后果:

  • macOS 上的 Pake 应用用不了通行密钥和 Touch ID 登录,因为系统只把它们提供给浏览器,以及和站点域名绑定的应用(Pake 的 FAQ)。
  • 系统自带的保存密码提示只在 Windows 上有,--password-autosave 在 macOS 和 Linux 上会被忽略。
  • WKWebView 自己不提供 HTTP 401 登录对话框,所以 Pake 提供 --basic-auth,在 macOS 上弹出凭据输入框,凭据只保留在当前会话里。

进程:为什么它不在应用进程里

Apple 的 WKProcessPool 页面说,WebKit 在独立进程里渲染网页视图的内容,不放在你应用的进程空间里,这样更安全也更稳定。这个类本身已经弃用,并注明创建多个进程池不再有任何效果,所以这一页只适合用来确认进程这个事实,不适合拿来做配置。

WebKit 自己的架构说明描述了三种角色。UI 进程代为处理对剪贴板、麦克风和摄像头这类系统资源的请求,每个页面加载在一个沙盒化的 WebContent 进程里,网络进程可以被多个 WebContent 进程共享,负责所有网络访问,管理磁盘缓存,并支撑 Web Storage 和 IndexedDB 这类存储接口。

Pake 应用外壳里是一个 WKWebView,它在单独的 WebContent 进程里渲染页面,网络请求再交给共享的 Networking 进程;WKWebView 配有一个网站数据存储,保存 Cookie 和缓存

页面跑在 WebKit 自己的进程里,不在应用进程内,重启后能留下什么由数据存储决定。

Pake 的 FAQ 把 macOS 上的这个进程叫作 WebContent,在内存上也是同一个说法,像 Gemini、Slack 或 ChatGPT 这样重的单页应用,它可能占用几百 MB,取决于引擎和页面,而不是 Pake。安装包大小和内存是两个数字,Tauri 和 Electron 的差别里有解释。

数据存储:Cookie 和登录状态放在哪

网页视图把 Cookie、缓存和其他站点数据放在 WKWebsiteDataStore 里。Apple 说不指定的话视图用默认存储,默认存储把数据持久地写到磁盘上,非持久存储只放在内存里,用来做隐私浏览。从 macOS 14 起,应用还可以按标识符要一个持久存储,Apple 说这是给多个配置档用的。

Pake 把自己的选项映射到这些上。在 Tauri 用的 wry 库里,也就是 Pake CLI 3.17.6 使用的 0.54.2 版本,--incognito 选非持久存储,只有应用在 macOS 14 或更高版本上提供了标识符才会用它,否则用默认存储。Pake 没有传标识符,所以 Mac 上的普通应用用默认存储,无痕应用什么都不留。命令行文档说无痕意味着没有 Cookie、本地存储和历史记录,图库里的微信应用每次启动都要扫码就是这个原因。

Windows 和 Linux 上 Pake 的做法不同,它在系统配置目录下设置一个以产品名命名的数据目录,每个应用在那边有自己的配置。两个应用意味着什么,可以看同一个网站用两个账号。

用户代理

Apple 的 customUserAgent 默认是 nil,applicationNameForUserAgent 让应用把自己的名字加进字符串。Pake 在创建窗口时把用户代理传进去,wry 用 setCustomUserAgent 应用它。在 Pake CLI 3.17.6 里,Mac 应用默认是一个 Safari 26.0 字符串,Windows 是 Chrome 147.0.0.0 字符串。这个字符串只是个标签,不是引擎,设置用户代理里有完整解释。

应用里的 Web Inspector

Apple 的 isInspectable 属性决定 Safari 的 Web Inspector 能不能打开一个视图,默认是 false,在 macOS 13.3 及以后可用,设为 true 后可以在 Safari 的开发菜单里选这个视图。菜单栏里没有开发菜单的话,Apple 的 Safari 帮助说选取 Safari 浏览器 > 设置,点按高级,再选择显示网页开发者功能。

Pake 的正常构建不会打开它。wry 里设置它以及 developerExtrasEnabled 偏好的代码,只在调试构建或带 devtools 特性时才编译,而 Pake 的 Cargo.toml 没有开这个特性。Pake 的 --debug 参数以调试模式构建(tauri build --debug),会打开 Toggle Developer Tools 菜单项,它在正常构建里是禁用的,Mac 上的快捷键是 Cmd Option I。所以想在 Pake 应用里检查某个站点,就用 --debug 重新构建一次:

pake https://example.com --name Example --debug

用检查器找出页面在应用里失败、在 Safari 里正常的那处差别,修好之后,发布时用不带 --debug 的构建。

Windows 和 Linux 上对应的东西

Pake 在每个系统上用该平台的网页视图,Tauri 的说明里列了。Windows 上是 WebView2,基于 Edge 的 Chromium,会自己更新,WebView2 是什么里有介绍。Linux 上是 WebKitGTK,版本取决于发行版带的是哪一个,Pake 的 FAQ 里提到它的辅助进程叫 WebKitWebProcess 和 WebKitNetworkProcess,这两个名字呼应了上面的 WebContent 和网络进程两种角色。

结果就是同一个 Pake 应用在三个系统上是三种引擎。在 Safari 里正常的网站通常在 Mac 上也正常,Windows 版要单独测试,Pake 是怎么工作的里也这么说。

站点表现不对时

先在同一台 Mac 的 Safari 里打开它,要是在 Safari 里也不行,问题就不在应用。要是 Safari 里正常、应用里不正常,按顺序查这几项:登录(登录问题说明,Google 的 OAuth 指南写明,iOS 和 macOS 开发者在 WKWebView 里打开授权请求时可能遇到 disallowed_useragent,所以 Google 登录在 Safari 里正常、在应用里失败是预料之中的),Cloudflare 或机器人验证,Pake 的 FAQ 说系统网页视图经常被它们标记,还有摄像头或麦克风权限没给,因为 macOS 上的 Pake 应用除非你传了 --camera 或 --microphone,否则不会去申请。

常见问题

WKWebView 和 Safari 是一回事吗?

用的是同一个 WebKit 引擎,但它只是那个视图,标签页、扩展、书签和 Safari 自己的设置都是 Safari 加上去的。

Pake 应用在 Mac 上能用 Chrome 的引擎吗?

Pake 在 macOS 上用 WKWebView,站点需要 Chromium 的话,可以看看基于 Chromium 的套壳工具。

在 Pake 中,网站数据保存在应用自己的存储里,Safari 里的登录不会自动带进来,需要在 Pake 应用里重新登录。WKWebsiteDataStore.default() 的“默认”指应用使用的持久存储,不是让应用读取 Safari 的登录。