Google Photos 帮助社区有个帖子,标题就几个字:怎么永久禁用备份照片的弹窗。点进去,没人回。连个“已收到”都没有。产品自己都不知道怎么把这弹窗永久摁死。我本来只是路过吃瓜,结果这问题挂了一天——我最近正好在摸 Notification.requestPermission() 这头牛,跟这帖子的味儿太像了:用户要的是“永久禁用”,系统给的是“弹一次,拒绝完我也装死”。你说气不气。
这头牛,我今天非钻穿不可。
先交代一句:那个帖子说的是 Google Photos 的备份提示,不是 web 通知,别混。但这两个东西背后是同一套逻辑:权限弹窗没有给用户一个明明白白的“永久禁用”按钮,只有“允许”和“阻止”。点完阻止,再想管理,入口藏得比什么都深。我先从手头那个 web 通知权限说起。
起了个本地页面,就一行:
Notification.requestPermission().then(console.log)
Chrome 上第一次点允许,控制台输出 granted。干净。
换隐身窗口点阻止,输出 denied。我还以为这跟 Cookie 一样,清掉站点数据就复位。结果清完 Cookie、LocalStorage、IndexedDB,Notification.permission 还是 denied。我有点不爽了——权限状态不跟站点数据走,它到底记在哪儿?
于是查 Permissions API:
const status = await navigator.permissions.query({ name: 'notifications' })
console.log(status.state)
结果印出来 prompt。
同一个页面、同一时刻,Notification.permission 明明打印的是 default,navigator.permissions.query 又冒出来个 prompt。这俩到底谁说了算?
我一开始真以为 Chrome 抽风。后来翻了半天规范才弄明白:两套 API 不是同一拨人写的,命名都没对齐。Notification.permission 用 default 表示“还没问过用户”,Permissions API 用 prompt 表示同一个状态。granted 和 denied 两边倒是统一。我卡在“default 和 prompt 到底是不是一回事”上卡了快半小时,最后确认:是。
就离谱。命名都能打架,活见鬼。
真正坑的不是命名,是这个状态一旦从 prompt(也就是 default)变成 denied,就再也回不去了。JavaScript 没有 API 能把权限状态改回去。用户第一次点了“阻止”,以后同源页面再调 Notification.requestPermission(),浏览器连问都不问,直接返回 denied。
我试过把 PermissionsStatus 对象当普通对象改,以为只是没找到 setter:
status.state = 'granted'
console.log(status.state) // 还是 denied
啥也没发生,哎。这玩意儿是只读的,浏览器不给任何口子改。
又试着循环调,想会不会哪次运气好又弹出来:
for (let i = 0; i < 5; i++) {
console.log(await Notification.requestPermission())
}
结果五次全是 denied,一个拒绝我五次。浏览器在权限这儿,铁了心不做“再给一次机会”。你只能去地址栏左边那个锁图标里,点开站点权限,把通知从“阻止”改回“询问”。这个入口,移动端藏得啊,我都不想说。
于是我把几个浏览器摸了个底:
| 浏览器 | 用户拒绝后 JS 能否再次弹窗 | 权限状态查询 | 备注 |
|---|---|---|---|
| Chrome | 不能 | permissions.query 完整 | 地址栏锁图标里可重置,入口还算好找 |
| Firefox | 不能 | 完整 | 设置里的权限管理页面直观 |
| Edge | 不能 | 完整 | 跟 Chrome 一致 |
| Safari | 不能 | 半吊子,桌面端有,iOS 上迷 | 通知设置入口藏得极深,经常找不到 |
Safari,又是它。这次它不是在渲染上搞鬼,是在权限管理入口上搞鬼。你有的时候在 iOS Safari 里想找个站点的通知设置,得一路点好几个层级,点到最后未必能找到。我确实要说它拉胯。
这里自首一句:Safari 桌面端我还没全测完,尤其 navigator.permissions.query({ name: 'notifications' }) 在 iOS 上的行为,昨天测到一半卡住了。今天先写到这,明天补。别学我,写到一半就发。
你肯定要问,这跟那个 Google Photos 帖子有什么关系?关系就是:用户想要的“永久禁用”不是一个二进制开关,是一套权限生命周期。用户点“阻止”,系统确实不再弹了——这个意义上算“永久”了。但这个“永久”是单向的,用户没有地方看到“哦,这弹窗已经被我永久禁了,我可以在哪儿改回来”。更糟的是,用户想主动禁用还没弹过的站点,居然没有入口。你得等它先弹你一脸,然后点阻止。这是把用户当傻子。
我越写越觉得,这种“弹一次,拒绝就装死,想管理没门”的设计,才是真正该钻穿的牛角。前端在这块能做的其实就两件事:要么别弹,要么弹了之后把用户的选择当回事,并且在设置里给个明明白白的入口。可多数时候业务方只想让你弹,弹一次算一次。至于用户想不想永久禁用?不去想。
Google Photos 那个帖子现在还没人回,也可能永远不会有人回。因为这个问题本身就不是“用户不会操作”,而是产品上根本没给答案。你不能让用户去地址栏左边那个锁图标里找备份弹窗的设置——那里根本没有。
最后,说个跟这头牛八竿子打不着的——我今天为了测权限,把自己一个用了两年的 Chrome 扩展的通知权限给重置了。结果它再也没弹过,我也没觉得少了什么。我甚至想不起来那个扩展为什么当初要通知权限。这大概就是“永久禁用”最好的下场:禁了就禁了,生活照旧。可普通用户根本摸不到这个下场。扯远了,反正——嘿嘿,下次见。
