Highlight
macOS Sonoma 支持将任意网站添加到 Dock 作为独立 Web App,拥有独立窗口、Cookie 和系统级权限管理;配合 Web App Manifest 可控制工具栏、链接跳转范围和 display 模式,Web Push 通知和 Badge 同时支持 macOS 和 iOS/iPadOS 的 Home Screen Web App。
核心内容
macOS 上的 Web App
macOS Sonoma 新增了 Web App 功能。在 Safari 中浏览任意网站时,选择 File > “Add to Dock…”,网站就会以独立应用的形式出现在 Dock 中。添加时会复制网站的 Cookie,所以用户通常保持登录状态。
Web App 拥有简化的工具栏,网站的主题色会融入工具栏背景。它与原生 macOS 应用一样支持 Stage Manager、Mission Control、Command+Tab 切换,可以从 Dock、Launchpad 和 Spotlight 打开。
权限管理也与原生应用一致。相机、麦克风和位置访问通过系统权限弹窗请求,在系统设置的 Privacy & Security 中管理。
(00:51)
Web App Manifest 定制
开发者可以通过 Web App Manifest 控制 Web App 的行为。在 HTML 的 head 中添加:
<link rel="manifest" href="/manifest.json">
Manifest 中的关键字段:
name:Web App 的名称(替代网页标题)display:显示模式,standalone表示无工具栏start_url:Web App 首次打开时加载的 URLscope:定义链接在 Web App 内打开的范围id:同一域名下区分不同 Web App 的唯一标识
(05:01)
display: standalone 的效果在不同平台有区别:
- macOS:不显示工具栏
- iOS/iPadOS:以 Home Screen Web App 形式打开,拥有独立的 Cookie 和存储,与浏览器隔离
如果需要在 iOS 上使用 Web Push 和 Badge,必须使用 standalone 模式。
链接跳转和 Scope
Web App 有一个关联的 scope。Scope 内的链接在 Web App 内打开,Scope 外的链接在默认浏览器中打开。默认 scope 是创建 Web App 时网页的 host。
可以通过 manifest 中的 scope 字段进一步限制。比如将 scope 设为 example.com/browserpets,那么 example.com/webkittens 的链接就会在默认浏览器中打开。
在 iOS 的 Home Screen Web App 中,Scope 外的链接会在 Safari View Controller 中打开。
(05:53)
认证和 Cookie
Web App 创建时会复制 Safari 中的 Cookie,所以用户通常保持登录。但 local storage 不会被复制。如果认证状态分散在 Cookie 和 local storage 中,用户需要重新登录。
建议将认证状态保存在 Cookie 中。OAuth 流程通常会在 Web App 内打开(通过启发式判断),如果发现问题可以向 Apple 反馈。window.open 打开的链接始终在 Web App 内,不受 scope 限制。
邮件中的自动登录链接会在默认浏览器中打开,不会自动登录到已有的 Web App。可以考虑在邮件中提供一次性验证码作为替代方案。更推荐的做法是采用 passkey 认证。
(07:37)
Web Push 通知和 Badge
macOS 上的 Web App 支持标准的 Web Push。如果已经按 web 标准实现了 Web Push,不需要额外工作即可在 macOS Web App 中使用。
通知会显示 Web App 的图标(而不是 Safari 图标),给用户正确的上下文。
声音控制遵循平台惯例:iOS/iPadOS 默认开启声音,macOS 默认关闭。可以通过 Notification API 的 silent 选项覆盖:
new Notification("New message", { silent: false });
Badge 在 macOS Web App 和 iOS Home Screen Web App 中都受支持。用户允许通知时,同时授予了 Badge 权限。Badge 可以在 Web App 打开时更新,也可以在后台处理 push 事件时更新。
(10:00)
Focus 模式集成
Web App 的通知与 Focus 模式集成。用户可以在不同 Focus 模式下设置不同的通知偏好,这些偏好会跨设备同步。
Manifest 中的 id 字段用于区分同一域名下的不同 Web App。比如一个域名下有 shop 和 forums 两个 Web App,可以给它们分别设置 id: "shop" 和 id: "forums",用户就能为它们设置不同的通知偏好。
Focus 设置按 name + id 同步。用户可以在同一设备上创建同一个网站的多个实例(如”Forums”和”Forums - Work”),分别设置不同的通知和 Focus 偏好。
(12:54)
新增 Web API
Safari 今年新增了以下 API:
- User Activation API:告知网站用户是否进行了瞬态或持久性激活,用于判断是否可以调用依赖用户激活的功能(如请求通知权限)
- Fullscreen API:Safari 16.4 在 macOS 和 iPadOS 中移除了前缀,支持标准 Fullscreen API
- Screen Orientation API:初步支持
type、angle属性和onchange事件
(14:29)
详细内容
Web App Manifest 完整示例
{
"name": "Browser Pets",
"short_name": "Pets",
"description": "A virtual pet community",
"start_url": "/browserpets",
"scope": "/browserpets",
"display": "standalone",
"theme_color": "#FF6B6B",
"background_color": "#FFFFFF",
"icons": [
{ "src": "/icon-192.png", "sizes": "192x192" },
{ "src": "/icon-512.png", "sizes": "512x512" }
],
"id": "browserpets"
}
关键点:
name用于 Dock/主屏幕显示,short_name用于空间不足时start_url是 Web App 首次打开时加载的页面scope限制在 Web App 内打开的链接范围,必须是 manifest URL 的子目录display: standalone在 macOS 隐藏工具栏,在 iOS 创建独立的 Home Screen Web Apptheme_color影响 macOS 工具栏的背景色id区分同一域名下的不同 Web App,用于 Focus 模式同步
HTML 中引用 Manifest
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="theme-color" content="#FF6B6B">
<title>Browser Pets - Your Virtual Pet Community</title>
<link rel="manifest" href="/manifest.json">
<link rel="apple-touch-icon" href="/icon-192.png">
</head>
<body>
<!-- 页面内容 -->
</body>
</html>
关键点:
theme-colormeta 标签与 manifest 中的theme_color保持一致apple-touch-icon用于 iOS 主屏幕图标manifestlink 必须放在head中
Web Push 和 Service Worker
// 注册 Service Worker
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
.then(registration => console.log('SW registered'))
.catch(err => console.error('SW registration failed', err));
}
// 请求推送通知权限
async function subscribeToPush() {
const registration = await navigator.serviceWorker.ready;
const subscription = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(
'BEl62i...'
)
});
// 将 subscription 发送到服务器
await fetch('/api/push-subscription', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(subscription)
});
}
关键点:
- Web Push 需要 Service Worker 支持
pushManager.subscribe需要用户激活(点击等交互)才能调用applicationServerKey是服务器的 VAPID 公钥userVisibleOnly: true表示每条推送都会显示通知
Service Worker 中处理推送:
// sw.js
self.addEventListener('push', event => {
const data = event.data?.json() ?? {};
event.waitUntil(
self.registration.showNotification(data.title, {
body: data.body,
icon: '/icon-192.png',
badge: '/badge-72.png',
silent: false,
data: { url: data.url }
})
);
});
// 点击通知打开对应页面
self.addEventListener('notificationclick', event => {
event.notification.close();
event.waitUntil(
clients.openWindow(event.notification.data.url)
);
});
关键点:
showNotification在 Service Worker 中显示系统通知silent: false覆盖 macOS 默认静音,播放通知声音badge是小图标,显示在通知栏和 Dock 角标中notificationclick处理用户点击通知的行为
Badge API 使用
// 设置 Badge 数字
navigator.setAppBadge(42);
// 清除 Badge
navigator.clearAppBadge();
// 在后台处理 push 时更新 Badge
self.addEventListener('push', event => {
event.waitUntil(
getUnreadCount().then(count => {
return self.navigator.setAppBadge(count);
})
);
});
关键点:
navigator.setAppBadge()设置 Dock(macOS)或主屏幕图标(iOS)的角标- 用户允许通知时自动获得 Badge 权限
- 在 Service Worker 中通过
self.navigator.setAppBadge()更新 - Badge 在 Web App 前台和后台都可以更新
认证状态管理
// 推荐:将认证状态保存在 Cookie 中
// 服务器端设置
// Set-Cookie: session=abc123; Path=/; HttpOnly; Secure; SameSite=Lax
// 避免:将认证状态分散在 localStorage 中
// 这样 Web App 创建时不会复制,用户需要重新登录
// 如果必须使用 localStorage,在首次加载时检查并引导重新认证
async function checkAuthState() {
const sessionCookie = document.cookie.match(/session=([^;]+)/);
const localToken = localStorage.getItem('auth_token');
if (sessionCookie && !localToken) {
// Cookie 存在但 localStorage 缺失,需要重新获取令牌
const response = await fetch('/api/refresh-token', {
credentials: 'include'
});
const data = await response.json();
localStorage.setItem('auth_token', data.token);
}
}
关键点:
- Web App 创建时复制 Cookie 但不复制 localStorage
- 纯 Cookie 认证的网站用户通常保持登录
- 混合认证需要在首次加载时检测并引导重新认证
- Passkey 是比邮件链接更安全和便捷的认证方案
核心启发
1. 为网站添加 Web App Manifest
- 做什么:为现有网站添加 manifest.json,配置
name、display: standalone、theme_color和icons - 为什么值得做:macOS 用户可以将你的网站添加到 Dock 作为独立应用,iOS 用户可以获得 Home Screen Web App 体验
- 怎么开始:创建 manifest.json 文件,在 HTML head 中添加 link 标签,测试 Safari 的 “Add to Dock” 和 “Add to Home Screen” 流程
2. 实现 Web Push 通知
- 做什么:为 Web App 添加推送通知功能,包括新消息提醒和 Badge 更新
- 为什么值得做:即使用户没有打开 Web App,也能通过通知重新 engage,Badge 提供未读数量提示
- 怎么开始:注册 Service Worker,使用 Push API 订阅,在服务器端集成 Web Push 协议发送通知,处理
silent选项适配不同平台
3. 用 Passkey 替代传统认证
- 做什么:将网站的登录方式从密码或邮件链接迁移到 Passkey
- 为什么值得做:Passkey 在 Web App 和浏览器中都能使用,不受 Cookie 复制限制,且比邮件验证码更安全便捷
- 怎么开始:集成 Web Authentication API,参考 WWDC22 “Meet passkeys” session 的实现指南
4. 为同一网站的多个功能创建独立 Web App
- 做什么:如果网站有多个独立功能(如商店和论坛),为每个功能创建独立的 Web App,用
id字段区分 - 为什么值得做:用户可以为不同功能设置独立的通知偏好和 Focus 模式,体验更接近原生应用
- 怎么开始:为每个功能创建独立的 manifest 文件,设置不同的
id和scope,确保链接在正确的 Web App 中打开
5. 适配 Scope 边界外的链接
- 做什么:明确网站的 scope 边界,确保外部链接(如第三方登录、合作伙伴页面)正确跳转
- 为什么值得做:scope 外的链接会在默认浏览器中打开,如果 OAuth 流程被误判为外部链接,用户可能无法完成登录
- 怎么开始:测试所有外部链接和 OAuth 流程,必要时使用
window.open()确保在 Web App 内打开,向 Apple 反馈误判的 OAuth 域名
关联 Session
- Meet Web Push for Safari — Web Push 标准在 Safari 中的实现
- Rediscover Safari developer features — Safari 开发者功能的全面更新
- What’s new in Web Inspector — Safari Web Inspector 的新调试功能
- What’s new in Safari extensions — Safari 扩展的新 API 和功能
- Meet passkeys — Passkey 认证机制介绍
评论
GitHub Issues · utterances