前端开发史上一直有一个近乎“白月光”的梦想:只写一套 HTML、CSS 和 JavaScript,就能在所有平台上获得媲美原生应用的体验。
免安装、跨平台、点开即用,这个目标听起来实在太诱人。过去近三十年里,工程师和科技公司为此尝试过不少方案,其中有些相当超前,有些简单粗暴,还有一些最终成了反面教材。回头再看,Web App 的演进史其实就是 Web 与原生系统不断靠近、碰壁,再重新寻找边界的过程。
洪荒时代:当网页拥有系统级权限
很多人以为 Web App 是移动互联网时代的产物,其实早在 20 世纪 90 年代末,微软就已经迈出过相当激进的一步。
Active Desktop 与 HTA
IE4 时代的 Active Desktop,允许用户把网页内容直接放到 Windows 桌面上。到了 1999 年,随 IE5 一同出现的 HTA(HTML Application)又把这条路向前推了一大步:开发者把文件扩展名从 .html 改成 .hta,Windows 就会通过 mshta.exe 把它作为独立应用运行。
HTA 最特别的地方不在于窗口像不像桌面软件,而在于它几乎没有普通网页的安全沙箱。脚本可以调用 ActiveX、FileSystemObject 等系统组件,读写文件、操作注册表,能力更接近一个本地 Windows 程序。
这当然很方便,也当然很危险。HTA 后来频繁出现在恶意脚本和企业内部管理工具中,可谓一半是生产力,一半是安全事故。不过,它确实很早就证明了一件事:用 Web 技术写桌面应用并不难,难的是如何在能力和安全之间划出边界。
Google Gears
2007 年,当时的浏览器还缺少可靠的本地存储、后台任务和离线能力,Google 索性推出了浏览器插件 Google Gears。它内置基于 SQLite 的本地数据库,提供 WorkerPool 处理后台任务,还能通过 LocalServer 缓存页面资源。
今天再看,这套组合很像后来 IndexedDB、Web Worker 和 Service Worker 的预演。Gears 本身没有成为标准,并在 HTML5 相关能力逐渐成熟后退出历史舞台,但它探索的方向几乎全部留了下来。
移动起义:被寄予厚望的 Web 应用
2007 年初代 iPhone 发布时,苹果并没有开放第三方原生 SDK。在当年的 WWDC keynote 上,乔布斯给出的方案是使用 Safari 支持的 Web 2.0 与 Ajax 技术:
You can write amazing Web 2.0 and Ajax apps that look exactly and behave exactly like apps on the iPhone. And these apps can integrate perfectly with iPhone services. They can make a call, they can send an email, they can look up a location on Google Maps. And guess what? There’s no SDK that you need!
—— Steve Jobs,WWDC 2007 keynote 视频片段,2007 年 6 月 11 日
大意是:开发者可以用 Web 2.0 和 Ajax 编写外观、行为都像 iPhone 内置软件的应用,还能与电话、邮件和 Google Maps 等系统服务配合,而且不需要另外安装 SDK。这里的“无需 SDK”并不是说网页拥有完整的原生能力,而是说苹果当时没有向第三方开放原生开发工具,开发者只能使用 iPhone 内置的 Safari 引擎和已有的 Web 标准。苹果当天发布的官方新闻稿也明确将这种方式称为“Third-Party Web 2.0 Applications”。换句话说,最初的 iPhone 第三方应用生态,官方设想的确是 Web。
这个设想没有维持太久。当时的移动处理器性能有限,浏览器 API 也很简陋,Web 应用很难在交互、性能和系统能力上与原生软件竞争。2008 年,苹果推出 iPhone SDK 和 App Store,平台重心很快转向了原生应用。此后应用商店带来的分发和内购模式,也让苹果缺少绕开商店、大力扶持 Web 应用的商业动力。
不过,Web 技术成为应用平台的尝试并没有就此停止。
- Palm webOS(2009):它建立在 Linux 之上,但应用层大量使用 HTML、CSS 和 JavaScript。卡片式多任务、手势交互等设计非常超前,后来也能在 iOS 和 Android 上看到它的影子。
- Firefox OS(2011):Mozilla 以 Boot to Gecko 项目为起点,试图用开放的 Web 技术构建面向低端智能手机的操作系统,并发展出
manifest.webapp等应用安装机制。
这些系统的失败不能简单归结为“网页太慢”。硬件性能、应用数量、渠道支持和生态规模都在其中起了作用。只是对当时的 Web 平台来说,要同时追赶成熟的原生体验和快速膨胀的应用生态,确实有些勉强。
史诗级翻车:AppCache
为了让网页离线运行,HTML5 曾提供过 Application Cache,通常简称 AppCache。开发者只要在页面中声明一个 .appcache 清单,就能告诉浏览器需要缓存哪些文件。想法很简单,实际使用起来却处处违背直觉,甚至催生了那篇著名的吐槽文章《Application Cache is a Douchebag》。
它最让人头疼的地方主要有三个。
- 页面会被自动加入缓存。 只要 HTML 引用了 manifest,当前页面就会成为所谓的 Master Entry。即使它没有显式写在清单里,浏览器也会缓存它。服务端已经更新,用户却可能仍在反复打开旧页面。
- 更新默认总是慢一拍。 用户访问时,浏览器先用现有缓存加载页面,再到后台检查并下载新版本。即使更新成功,当前页面通常仍在使用旧资源,往往要等下一次刷新才能看到新版本。
- 更新是全有或全无的。 清单中只要有一个资源下载失败,例如某张图片临时返回 404,整次更新就会被放弃。浏览器继续保留旧缓存,开发者还不容易看出问题究竟出在哪里。
AppCache 试图用一份声明式清单包办离线策略,却把太多关键行为藏在浏览器内部。它看似减少了代码,实际上增加了大量无法精细控制的状态。后来它被废弃并逐步移出浏览器,也就不令人意外了。
现代 PWA:把控制权还给开发者
2015 年,设计师 Frances Berriman 和 Google 工程师 Alex Russell 提出了 Progressive Web Apps,也就是 PWA 这个名称。它并不是某一项单独的技术,而是一组让 Web 应用逐步获得安装、离线、通知等能力的设计理念和 Web 标准。
其中最关键的变化是 Service Worker。与 AppCache 的静态黑盒不同,Service Worker 允许开发者在 JavaScript 中拦截 fetch 请求,并配合 Cache Storage 自己决定缓存什么、何时更新、网络失败后返回什么。控制权终于回到了应用手中。
代价也随之而来:自由度越高,需要处理的状态就越多。一个考虑不周的缓存策略,仍然可能让用户停留在旧版本,甚至出现页面和资源版本不一致造成的白屏。Service Worker 解决了 AppCache 无法控制的问题,却没有消除缓存一致性本身的复杂性。
而且,PWA 推行多年后也没有如愿取代原生应用,原因并不只在技术层面:
- 应用商店仍然掌握着主要分发入口。 原生应用的安装、支付、订阅和账号体系已经形成成熟闭环,Web 应用很难单靠技术优势改变用户习惯。
- 平台支持长期不对等。 iOS 这些年陆续补上了 Web Push、图标角标等能力,但在存储、后台运行和系统 API 方面,Web 应用与原生应用之间仍有明显差距。
- 替代方案已经足够成熟。 国内有依托超级应用生态的小程序,跨平台开发还有 React Native、Flutter、Electron 和 Tauri。PWA 只是众多选择中的一种,不再是唯一答案。
走向务实:今天应该怎样看待 Web App
如今再谈 Web App,已经没有必要背负“取代原生”的宏大目标。根据产品需求选用合适的能力,反而更符合 Web 渐进增强的本意。
如果应用不需要离线工作,就不必为了 PWA 这个名号强行加入复杂的 Service Worker。通过规范的 Web App Manifest、HTTPS 和必要的图标配置,现代浏览器已经可以提供安装入口和独立窗口体验;theme-color 等设置还可以进一步改善与系统界面的融合。不同平台的安装条件仍有差异,但这件事早已不必与离线缓存绑定。
如果只需要系统通知,也可以让 Service Worker 专注承担 Web Push 的后台入口,而不接管应用资源。需要离线能力时,再根据内容特点选择 Cache First、Network First 或其他策略,并明确设计更新和回退流程。
与此同时,VS Code for the Web、Figma、Notion 等产品也说明,Web 最有优势的地方未必是模仿手机上的原生应用,而是让复杂的生产力工具跨越操作系统,通过一个链接迅速抵达用户。至于是否安装到桌面,反而只是使用方式上的选择。
从 1999 年几乎可以直接操作系统的 HTA,到今天强调权限边界与渐进增强的 PWA,Web App 绕了很长一圈。它最终证明的或许不是“网页也能变成原生应用”,而是另一件更朴素的事:无论使用什么设备,只要打开一个 URL,就能进入同一个应用、看到同一份内容。
这种轻盈和自由,才是 Web 最难被替代的地方。