AI编码助手确实能为开发人员节省大量时间——不过,直到你查看生成的代码时,才会发现它们使用的编程模式仍然属于2019年的水平。
目录:
为什么AI助手会默认使用旧代码模式?
所有大型语言模型都是从互联网上学习知识的,而互联网的发展速度极其迅速。新的浏览器API在正式推出后,往往需要数年时间才能有足够的教程、Stack Overflow上的解答以及实际应用案例被纳入训练数据中。
在现代Web指南之前的实现:工具提示组件
任务要求: “开发一个工具提示组件,当鼠标悬停在按钮上时,该组件会出现在按钮上方。”
相关的HTML代码设计得相当合理。CSS通过`position: absolute`来控制组件的定位效果,还实现了不透明度的动画效果,并正确地使用了`role=”tooltip”`和`aria”description”`属性。接下来就是JavaScript部分的代码了:
// ❌ 在现代Web指南之前的实现——一个完全用JavaScript构建的交互管理层
document.addEventListener('DOMContentLoaded', () => {
const containers = document.querySelectorAll('.tooltip-container');
containers.forEach(container => {
const trigger = container.querySelector('.tooltip-trigger');
const tooltip = container.querySelector('.tooltip-content');
const forceHide = () => tooltip.classList.add('js-hidden');
const resetVisibility = () => tooltip.classList.remove('js-hidden');
// 使用Escape键关闭工具提示
trigger.addEventListener('keydown', (e) => {
if (e.key === 'Escape') { forceHide(); e.preventDefault(); }
});
trigger.addEventListener('blur', resetVisibility);
container.addEventListener('mouseleave', resetVisibility);
container.addEventListener('mouseenter', resetVisibility);
// 触屏设备上的交互处理
trigger.addEventListener('touchstart', (e) => {
const isVisible = !tooltip.classList.contains('js-hidden') && getComputedStyle(tooltip).visibility === 'visible';
if (isVisible) { forceHide(); } else { dismissAllTooltips(); resetVisibility(); }
}, { passive: true });
});
function dismissAllTooltips() {
document.querySelectorAll('.tooltip-content').forEach(t => t.classList.add('js-hidden'));
}
document.addEventListener('click', (e) => {
if (!e.targetclosest('.tooltip-container')) {
document.querySelectorAll('.tooltip-content').forEach(t => t.classList.remove('js-hidden'));
}
});
});
问题并不在于上述代码有错误——其实完全没有,这些代码是可以正常运行的。问题在于这种实现方式暴露出了一些本质性问题:由于CSS中的`:hover`和`:focus-within`选择器无法处理使用Escape键关闭工具提示、通过触摸操作切换显示状态,或者点击按钮外部的情况,因此开发者不得不另外编写JavaScript代码来管理工具提示的状态。这样一来,工具提示的显示状态就被分成了两个系统来处理,而这两个系统必须保持同步。`js-hidden`这个类的存在,正是为了让JavaScript能够覆盖CSS的设置。
如果你现在感兴趣的话,可以继续阅读在安装了现代Web指南之后,更新后的工具提示组件代码。
接下来,让我们看看在没有现代Web指南的情况下,开发者是如何实现toast通知功能的。
在现代Web指南之前的实现:带有退出动画的toast通知
任务要求: “开发一个toast通知系统,使得通知在消失之前会先淡出。”
// ❌ 在 Modern Web Guidance 出现之前——JavaScript 完全掌控了整个动画生命周期
const dismissToast = (toast) => {
if (toast.classList.contains('toast-fade-out')) return;
// 1. 添加 ‘toast-fade-out’ 类以触发 CSS 过渡效果
toast.classList.add('toast-fade-out');
// 2. 等待过渡效果完成,然后再从 DOM 中移除该元素
const handleUnmount = (e) => {
if (e.propertyName === 'opacity' || e.propertyName === 'transform') {
toast.removeEventListener('transitionend', handleUnmount);
toast.remove();
}
};
toast.addEventListener('transitionend', handleUnmount);
// 3. 如果 ‘transitionend’ 事件没有触发,就使用 setTimeout 进行回退处理
setTimeout(() => {
if (toast.parentNode) toast.remove();
}, 400);
};
// 4 秒后自动移除该元素
autoDismissTimer = setTimeout(() => {
dismissToast(toast);
}, 4000);
回顾上面的代码:这种设计模式非常常见,而且确实有效。但请注意,为了处理一个本质上与动画时机相关的问题,竟然需要使用如此大量的 JavaScript 代码。
首先,该机制会添加一个 CSS 类来启动过渡效果;然后通过监听 ‘transitionend’ 事件来确定何时应该移除该元素;如果 ‘transitionend’ 事件没有触发,就会使用 setTimeout 进行回退处理;最后还会再设置一个定时器来实现自动移除功能。
JavaScript 与 CSS 之间存在着紧密的关联。一旦更改 CSS 中的过渡效果持续时间,就必须相应地调整 JavaScript 中的定时器设置。
如果你现在感兴趣的话,可以继续阅读 在安装了 Modern Web Guidance 后,更新后的 Toast 通知代码示例。
这两个例子都有一个共同点:它们都是通过编写 JavaScript 代码来弥补浏览器本身无法处理的功能缺失。
什么是 Modern Web Guidance (MWG)?
Modern Web Guidance 是一个由 Google Chrome 团队和 Microsoft Edge 团队共同支持的开源项目。与其让系统自己去判断现代浏览器平台提供了哪些功能,不如为它提供一份结构清晰、经过专家审核的参考文件,将这些常见的开发场景与相应的解决方案对应起来。
这个参考文件以 代理脚本技能 的形式存在,也就是一个名为 `SKILL.md` 的文件。这个文件会被你的编码代理在生成代码之前读取。可以把它看作是专门为你的项目编写的操作手册,它告诉代理哪些现代 API 存在,以及应该在什么情况下使用它们。这种机制能够以一种单行指令无法实现的方式,增加使用现代平台解决方案的概率。
从底层来看,这一机制分为三个步骤来执行:
-
当任务与网页相关时,你的编码代理会激活这个技能。
-
代理会运行 `modern-web-guidance search “<查询内容>“` 这个命令,这是一个使用离线 TensorFlow.js 模型进行的本地语义搜索。这个过程不需要 API 密钥,也不会产生网络请求。
-
之后,代理会通过 `modern-web-guidance retrieve “<指南ID>“` 来获取相应的指导信息,并将这些信息直接插入到它的上下文窗口中,从而让开发者能够看到相关的注意事项、陷阱以及回退策略。
目前有两种技能包可供选择。modern-web-guidance涵盖了现代浏览器API、CSS布局系统、性能优化、无障碍功能以及内置的AI API,这正是大多数开发者所需要的。
chrome-extensions则涉及Manifest V3规范、后台工作进程以及Chrome Web Store的发布流程。根据早期测试数据,当开发工具安装了该技能包后,在遵循现代最佳实践方面,其合规性提升了37个百分点。
如何安装Modern Web Guidance
通用安装命令(适用于任何开发工具)如下:
npx modern-web-guidance@latest install
这个命令会运行一个交互式向导,它会自动检测你的开发工具类型,询问你需要哪些技能包,然后将SKILL.md文件放置在正确的位置。需要注意的是,这个命令行工具是完全离线的,也不需要任何外部依赖或API密钥。
使用Claude Code进行安装的步骤如下:
#1. 在市场中添加该插件:/plugin marketplace add GoogleChrome/modern-web-guidance
#2. 安装插件:/plugin install modern-web-guidance@googlechrome
#3. 重新加载插件:/reload-plugins
安装完成后,请确认你的项目根目录下是否存在/.claude/skills/文件夹,其中确实包含了相应的技能文件。Claude Code就是从这些文件中读取技能信息的。
需要特别说明的是,Modern Web Guidance已经列在技能市场中了。你只需搜索“modern-web-guidance”并点击“安装”按钮即可完成安装过程,完全不需要使用命令行工具。
如果使用GitHub Copilot的命令行接口进行安装,步骤也是相同的:
#1. 在市场中添加该插件:/plugin marketplace add GoogleChrome/modern-web-guidance
#2. 安装插件:/plugin install modern-web-guidance@googlechrome
对于Vercel Agent来说,安装方法也是:
npx skills add GoogleChrome/modern-web-guidance
而对于Google Antigravity来说,也可以直接在应用程序内部进行一键安装。
安装Modern Web Guidance后,实际会发生哪些变化?
之前我们了解到,在没有安装Modern Web Guidance的情况下,Tooltip和Toast Notification组件在接收到相关提示时会使用一些过时的技术。但当安装了该技能包之后,这些组件会使用完全不同的技术来处理同样的任务。
安装后的变化:Tooltip组件的处理方式
在使用Modern Web Guidance的情况下,相同的Tooltip提示不会生成任何JavaScript代码。相反,开发工具会同时使用popover="hint"这个API来实现悬停/聚焦时提示内容的显示,同时还会使用interestfor(即Interest Invokers API)来在HTML中明确指定提示内容出现的时机。
<!-- ✅ 安装Modern Web Guidance后——使用声明式HTML代码,完全不生成JavaScript -->
<div class="tooltip-wrapper">
<button
id="btn-deploy"
class="btn-trigger"
interestfor="tooltip-deploy"
>
部署应用
</button>
<div popover="hint" id="tooltip-deploy" class="tooltip-content">>
立即将代码更改部署到线上环境
<>/div>
</div>
/* 将锚点定位相关代码关联到触发器上 */
#btn-deploy {
anchor-name: --tooltip-deploy;
}
#tooltip-deploy {
position-anchor: --tooltip-deploy;
}
.tooltip-content {
position: absolute;
bottom: anchor(top);
left: anchor(center);
transform: translateX(-50%) translateY(8px);
opacity: 0;
transition: opacity 0.2s ease,
display 0.2s allow-discrete,
overlay 0.2s allow-discrete;
}
.tooltip-content:popover-open {
opacity: 1;
transform: translateX(-50%) translateY(-12px);
}
@starting-style {
.tooltip-content:popover-open {
opacity: 0;
transform: translateX(-50%) translateY(8px);
}
}
js-hidden 类已经被移除,dismissAllTooltips() 函数也被去掉了,touchstart 处理程序以及点击外部区域的检测机制也都不再存在了。
popover="hint" 这一属性能够让浏览器自动实现隐藏功能:当鼠标悬停在该元素上时,系统会处理显示/隐藏逻辑;焦点管理、通过按 Esc 键隐藏通知,以及触摸操作的相关行为,都无需编写任何 JavaScript 代码。@starting-style 规定了元素进入可见状态时的动画效果,而 allow-discrete 则负责处理元素退出可见状态时的动画效果,因此这两种过渡效果完全由 CSS 来控制。
关于浏览器兼容性的说明: Interest Invokers API(interestfor)目前仅在 Chrome 浏览器中可用,并且需要通过特定的标志来启用该功能;在 unpkg.com/interestfor 这个地址上也有相应的polyfill代码。CSS Anchor Positioning技术属于Baseline 2025标准的一部分,相关代理工具也会在输出结果中包含这些 polyfill 代码。在正式发布产品之前,请先访问 caniuse.com/css-anchor-positioning 来检查您的浏览器是否支持这些功能。
需要特别注意的是:在这两个 API 中,CSS Anchor Positioning技术已经可以在稳定的浏览器中使用了,而 interestfor 则还处于实验阶段。虽然有 polyfill 代码可以覆盖它的功能,但请将其视为平台未来发展方向的一个预览版本,而不是可以直接在生产环境中使用的功能。
使用退出动画后的提示效果
在采用了 Modern Web Guidance 设计方案后,相同的提示框会使用 popover="manual" 这一属性来控制其显示方式,而不是通过修改 CSS 类来改变显示效果。浏览器会自动处理相关的渲染和堆叠逻辑。
// ✅ 在采用了 Modern Web Guidance 设计方案后——浏览器会负责处理元素的显示/隐藏逻辑;JavaScript 只负责控制自动隐藏的时间
const createToast = (type) => {
const toast = document.createElement('div');
toast.setAttribute('popover', 'manual');
toast.className = `toast toast-${type}`;
toast.innerHTML = `