关于 best-practices
面向开发者的 Web 标准审查技能,聚焦安全性、浏览器兼容性和代码质量。审查流程结合页面 Lighthouse 证据,以及对依赖项、响应头、配置和源码的针对性检查。
主要功能
- 01基于证据的 Lighthouse 审查流程
- 02安全性与响应头检查
- 03浏览器兼容性指导
- 04弃用 API 检查
- 05代码质量与错误处理检查
适用场景
- 01部署前审查网站
- 02调查浏览器控制台或网络故障
- 03检查安全响应头和依赖项
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ addyosmani/ web-quality-skills --skill best-practices安装后在 Claude Code 中输入/best-practices确认已加载。
附带文件references/SECURITY.md
详细介绍
最佳实践
基于 Lighthouse 最佳实践审计的现代 Web 开发标准。涵盖安全性、浏览器兼容性和代码质量模式。
以证据为依据的审计工作流
当渲染后的页面可用时:
- 在具备相应能力时,运行实时 Lighthouse 最佳实践审计;使用 Chrome DevTools MCP 时,调用
lighthouse_audit。普通页面加载时使用导航模式,必须保留当前状态时使用快照模式。 - 检查列出的控制台和网络故障,仅在有助于支持某项发现时获取具体详情。
- 通过检查依赖项、标头、配置和源代码,补充运行时证据;Lighthouse 并非完整的安全评估。
- 修复相关代码,重新运行相同的审计,并将安全问题与风格偏好分开。
如果无法使用实时工具,请使用 Lighthouse CLI,并有针对性地检查依赖项和标头。切勿将较高的 Lighthouse 分数作为应用安全的证明。
安全性
当安全性属于审查范围,或实时审计发现相关故障时,请阅读安全参考。其中涵盖 HTTPS/HSTS、CSP 和 Trusted Types、子资源完整性、标头、依赖项、净化和 Cookie。
至少应做到:
- 使用 HTTPS,避免混合内容。 确认所有相关子域名均支持 HTTPS 后,再添加 HSTS。
- 将严格的 CSP 视为纵深防御。 优先使用 nonce 或哈希,并先以仅报告模式进行测试,再启用强制执行。
- 净化不受信任的 HTML,并防范 DOM XSS 注入点。 不需要标记时,优先使用文本 API。
- 固定并审查第三方代码。 在交付模式支持的情况下使用 SRI,并及时修补依赖项。
- 在运行时验证响应标头。 仅检查源代码配置无法证明部署的页面实际发送了什么。
浏览器兼容性
Doctype 声明
<HTML>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
<!DOCTYPE html>
<html lang="en">
字符编码
<html>
<head>
<title>Page</title>
<meta charset="UTF-8">
</head>
<html>
<head>
<meta charset="UTF-8">
<title>Page</title>
</head>
Viewport meta 标签
<head>
<title>Page</title>
</head>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Page</title>
</head>
特性检测
// ❌ Browser detection (brittle)
if (navigator.userAgent.includes('Chrome')) {
// Chrome-specific code
}
// ✅ Feature detection
if ('IntersectionObserver' in window) {
// Use IntersectionObserver
} else {
// Fallback
}
// ✅ Using @supports in CSS
@supports (display: grid) {
.container {
display: grid;
}
}
@supports not (display: grid) {
.container {
display: flex;
}
}
Polyfill(按需)
优先在构建时打包 polyfill(使用 Babel/SWC + core-js,或 @vitejs/plugin-legacy),并根据支持的浏览器列表设置目标范围。这样可以完全消除运行时检查,并避免向现代浏览器发送 polyfill 字节。
如果必须在运行时加载 polyfill,请追加一个 script 元素——切勿使用 document.write(它会阻塞解析器,并且在异步或延迟上下文中无法正常工作):
<script>
if (!('fetch' in window)) {
const s = document.createElement('script');
s.src = '/polyfills/fetch.js';
s.defer = true;
document.head.appendChild(s);
}
</script>
切勿从你无法控制的第三方 CDN 加载 polyfill。 polyfill.io 服务在 2024 年年中遭到供应链攻击,并被用于向约 10 万个网站提供恶意软件。请自行托管,或使用经过审核的镜像(例如 Cloudflare 的 cdnjs polyfill 构建版本)——并使用子资源完整性固定版本。
已弃用的 API
避免使用这些 API
// ❌ document.write (blocks parsing)
document.write('<script src="..."></script>');
// ✅ Dynamic script loading
const script = document.createElement('script');
script.src = '...';
document.head.appendChild(script);
// ❌ Synchronous XHR (blocks main thread)
const xhr = new XMLHttpRequest();
xhr.open('GET', url, false); // false = synchronous
// ✅ Async fetch
const response = await fetch(url);
// ❌ Application Cache (deprecated)
<html manifest="cache.manifest">
// ✅ Service Workers
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js');
}
事件监听器的被动模式
// ❌ Non-passive touch/wheel (may block scrolling)
element.addEventListener('touchstart', handler);
element.addEventListener('wheel', handler);
// ✅ Passive listeners (allows smooth scrolling)
element.addEventListener('touchstart', handler, { passive: true });
element.addEventListener('wheel', handler, { passive: true });
// ✅ If you need preventDefault, be explicit
element.addEventListener('touchstart', handler, { passive: false });
控制台与错误
不出现控制台错误
// ❌ Errors in production
console.log('Debug info'); // Remove in production
throw new Error('Unhandled'); // Catch all errors
// ✅ Proper error handling
try {
riskyOperation();
} catch (error) {
// Log to error tracking service
errorTracker.captureException(error);
// Show user-friendly message
showErrorMessage('Something went wrong. Please try again.');
}
错误边界(React)
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, info) {
errorTracker.captureException(error, { extra: info });
}
render() {
if (this.state.hasError) {
return <FallbackUI />;
}
return this.props.children;
}
}
// Usage
<ErrorBoundary>
<App />
</ErrorBoundary>
全局错误处理器
// Catch unhandled errors
window.addEventListener('error', (event) => {
errorTracker.captureException(event.error);
});
// Catch unhandled promise rejections
window.addEventListener('unhandledrejection', (event) => {
errorTracker.captureException(event.reason);
});
源映射
生产环境配置
// ❌ Source maps exposed in production
// webpack.config.js
module.exports = {
devtool: 'source-map', // Exposes source code
};
// ✅ Hidden source maps (uploaded to error tracker)
module.exports = {
devtool: 'hidden-source-map',
};
// ✅ Or no source maps in production
module.exports = {
devtool: process.env.NODE_ENV === 'production' ? false : 'source-map',
};
向错误跟踪工具上传生产环境映射文件时,移除 sourcesContent。默认情况下,打包工具会将完整的原始源代码嵌入 .map 文件——任何获取到映射文件的人(包括通过配置错误的上传步骤获取的人)都能看到未压缩的代码。请配置打包工具省略 sourcesContent,或使用 Sentry/Bugsnag CLI 标志,在上传时完成此操作。
对于 Vite,优先使用 sourcemap: 'hidden',而不是 'true',以免将 //# sourceMappingURL= 注释写入打包文件。
性能最佳实践
避免阻塞模式
// ❌ Blocking script
<script src="heavy-library.js"></script>
// ✅ Deferred script
<script defer src="heavy-library.js"></script>
// ❌ Blocking CSS import
@import url('other-styles.css');
// ✅ Link tags (parallel loading)
<link rel="stylesheet" href="styles.css">
<link rel="stylesheet" href="other-styles.css">
高效的事件处理器
// ❌ Handler on every element
items.forEach(item => {
item.addEventListener('click', handleClick);
});
// ✅ Event delegation
container.addEventListener('click', (e) => {
if (e.target.matches('.item')) {
handleClick(e);
}
});
内存管理
// ❌ Memory leak (never removed)
const handler = () => { /* ... */ };
window.addEventListener('resize', handler);
// ✅ Cleanup when done
const handler = () => { /* ... */ };
window.addEventListener('resize', handler);
// Later, when component unmounts:
window.removeEventListener('resize', handler);
// ✅ Using AbortController
const controller = new AbortController();
window.addEventListener('resize', handler, { signal: controller.signal });
// Cleanup:
controller.abort();
代码质量
有效的 HTML
<div id="header">
<div id="header">
<ul>
<div>Item</div>
</ul>
<a href="/"><button>Click</button></a>
<header id="site-header">
</header>
<ul>
<li>Item</li>
</ul>
<a href="/" class="button">Click</a>
语义化 HTML
<div class="header">
<div class="nav">
<div class="nav-item">Home</div>
</div>
</div>
<div class="main">
<div class="article">
<div class="title">Headline</div>
</div>
</div>
<header>
<nav>
<a href="/">Home</a>
</nav>
</header>
<main>
<article>
<h1>Headline</h1>
</article>
</main>
图片宽高比
<img src="photo.jpg" width="300" height="100">
<img src="photo.jpg" width="300" height="225">
<img src="photo.jpg" style="width: 300px; height: 200px; object-fit: cover;">
权限与隐私
正确请求权限
// ❌ Request on page load (bad UX, often denied)
navigator.geolocation.getCurrentPosition(success, error);
// ✅ Request in context, after user action
findNearbyButton.addEventListener('click', async () => {
// Explain why you need it
if (await showPermissionExplanation()) {
navigator.geolocation.getCurrentPosition(success, error);
}
});
权限策略
<meta http-equiv="Permissions-Policy"
content="geolocation=(), camera=(), microphone=()">
<meta http-equiv="Permissions-Policy"
content="geolocation=(self 'https://maps.example.com')">
审计清单
安全性(关键)
- 已启用 HTTPS,无混合内容
- 无存在漏洞的依赖项(
npm audit) - 已配置 CSP 标头(包含
frame-ancestors、base-uri、form-action) - 已强制执行
require-trusted-types-for 'script'(或在推出期间设为仅报告模式) - 第三方
<script>/<link rel="stylesheet">已使用 SRI 哈希固定 - 已设置安全标头(HSTS、X-Content-Type-Options、Referrer-Policy)
- 未暴露源映射(上传的源映射也已剥离
sourcesContent)
兼容性
- 使用有效的 HTML5 doctype
- 在 head 中首先声明字符集
- 存在 viewport meta 标签
- 未使用已弃用的 API
- 滚动和触摸事件使用被动事件监听器
代码质量
- 无控制台错误
- HTML 有效(无重复 ID)
- 使用语义化 HTML 元素
- 错误处理得当
- 在组件中清理内存
用户体验
- 无侵入式插页广告
- 在适当的上下文中请求权限
- 错误消息清晰
- 图片宽高比适当
工具
| 工具 | 用途 |
|---|---|
npm audit | 检查依赖项漏洞 |
| SecurityHeaders.com | 分析标头 |
| W3C Validator | 验证 HTML |
实时 Lighthouse 审计(Chrome DevTools MCP:lighthouse_audit) | 为 Agent 检查渲染后的最佳实践 |
| Lighthouse CLI | 最佳实践审计的备用方案 |
| Observatory | 安全扫描 |