渐进增强是应对差异的基本策略
别名: 能力检测 · 功能探测 · 优雅降级 · feature detection
概念解释
面对标准与实现分叉、移动视口和输入法各行其是,Web 上站得住的策略不是按浏览器名字开分支,而是渐进增强(progressive enhancement):先用 HTML 把任务做成可完成的文档(链接能跳、表单能提交、内容能读),再用 CSS 和 JS 在能力存在时叠上方便。增强失败时,任务还在;嗅探 User-Agent 去做「如果是某浏览器就换一套」,会在引擎改默认、内嵌 WebView 撒谎、或新引擎出现时整支分叉腐烂。
它处理的是「差异面前默认站哪一边」,不是去列举视口和键盘的具体机制,也不是去证明标准与实现有差距。差距和视口问题是病情,渐进增强是长期还用得上的治法。
机制
文档层依赖的是各引擎已经对齐的那一小截:链接、语义结构、表单的 GET/POST、能被读屏读到的文本。这一层不请求特定控件外观,也不请求可视视口 API。增强层通过能力检测(@supports、对象探测、媒体查询)询问「这件事现在能不能做」,而不是询问「你是谁」。能做,就加上客户端校验、本地存储、更顺的滚动和自定义控件;不能做,用户仍可把表单提交给服务器、仍可顺着链接把任务做完。
嗅探会腐烂,是因为它把暂时的实现清单写成了永久身份。引擎会改 UA 字符串、会共用内核、会在系统 WebView 里报一个桌面品牌。能力检测跟着当前进程里真实存在的 API 走,清单可以过期,检测结果是当下的。所谓优雅降级如果只是「先做完整客户端再砍功能」,砍的顺序往往是从边缘引擎开始丢任务;渐进增强把顺序倒过来——先保证任务,再把方便当作可拆卸的层。
边界
必须依赖某一 API 才能成立的应用(实时协作画布、纯客户端编解码)没有可提交的文档层,渐进增强会退化为「不支持就明确拒绝」,而不是假装有一个能用的表单。内嵌在超级应用或原生壳里、且壳保证内核版本的 Web 页面,可以少做几层检测,但仍应保留文档层,因为壳会升级或更换内核。无障碍并不自动随着增强到来:自定义控件若只在 JS 层出现,关掉脚本的辅助技术就只剩下一堆空 div。增强必须把名称、角色和键盘操作一起加上,否则「更漂亮」的那一层反而是排除。
怎么落地
- 主任务在无 CSS、无 JS 或 JS 失败时仍能靠链接和表单提交完成;把客户端校验、无限滚动、自定义日期控件标成增强,而不是标成前提。
- 用
@supports和运行时探测决定是否启用增强,禁止按 UA 字符串分支布局或功能。 - 自定义控件在增强层提供时,同步提供键盘操作和可被辅助技术读取的名称与角色;否则留在原生控件上。
- 验证:在一种引擎里关掉 JS 走完注册、下单或提交;再在不支持某增强 API 的引擎里走同一条路径,确认任务完成而不是白屏。用 UA 伪装成另一种浏览器,确认界面并不因此换成另一套逻辑。任何「必须是某浏览器才能开始」的门,都说明策略退回了嗅探。