关于 best-practices
这项 Web 开发参考技能基于 Lighthouse 最佳实践审计,面向需要检查网站安全性、浏览器兼容性、性能和代码质量的开发者,并提供指导与审查清单。
主要功能
- 01安全标头与输入安全
- 02浏览器兼容性指导
- 03过时 API 的替代方案
- 04错误处理与源映射
- 05性能和内存管理实践
- 06审计清单与工具
适用场景
- 01检查 Web 应用安全性
- 02核对浏览器兼容性
- 03减少生产环境错误
- 04审计性能与代码质量
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ tech-leads-club/ agent-skills --skill best-practices安装后在 Claude Code 中输入/best-practices确认已加载。
详细介绍
最佳实践
基于 Lighthouse 最佳实践审计的现代 Web 开发标准。涵盖安全性、浏览器兼容性和代码质量模式。
安全性
全面使用 HTTPS
强制使用 HTTPS:
<img src="http://example.com/image.jpg" />
<script src="http://cdn.example.com/script.js"></script>
<img src="https://example.com/image.jpg" />
<script src="https://cdn.example.com/script.js"></script>
<img src="//example.com/image.jpg" />
HSTS 标头:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
内容安全策略(CSP)
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self';
script-src 'self' https://trusted-cdn.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
connect-src 'self' https://api.example.com;"
/>
CSP 标头(推荐):
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-abc123' https://trusted.com;
style-src 'self' 'nonce-abc123';
img-src 'self' data: https:;
connect-src 'self' https://api.example.com;
frame-ancestors 'self';
base-uri 'self';
form-action 'self';
在内联脚本中使用 nonce:
<script nonce="abc123">
// This inline script is allowed
</script>
安全标头
# Prevent clickjacking
X-Frame-Options: DENY
# Prevent MIME type sniffing
X-Content-Type-Options: nosniff
# Enable XSS filter (legacy browsers)
X-XSS-Protection: 1; mode=block
# Control referrer information
Referrer-Policy: strict-origin-when-cross-origin
# Permissions policy (formerly Feature-Policy)
Permissions-Policy: geolocation=(), microphone=(), camera=()
避免使用存在漏洞的库
# Check for vulnerabilities
npm audit
yarn audit
# Auto-fix when possible
npm audit fix
# Check specific package
npm ls lodash
保持依赖项更新:
// package.json
{
"scripts": {
"audit": "npm audit --audit-level=moderate",
"update": "npm update && npm audit fix"
}
}
应避免的已知漏洞模式:
// ❌ Prototype pollution vulnerable patterns
Object.assign(target, userInput)
_.merge(target, userInput)
// ✅ Safer alternatives
const safeData = JSON.parse(JSON.stringify(userInput))
输入清理
// ❌ XSS vulnerable
element.innerHTML = userInput
document.write(userInput)
// ✅ Safe text content
element.textContent = userInput
// ✅ If HTML needed, sanitize
import DOMPurify from 'dompurify'
element.innerHTML = DOMPurify.sanitize(userInput)
安全 Cookie
// ❌ Insecure cookie
document.cookie = "session=abc123";
// ✅ Secure cookie (server-side)
Set-Cookie: session=abc123; Secure; HttpOnly; SameSite=Strict; Path=/
浏览器兼容性
Doctype 声明
<html lang="en">
<head>
<title>Page</title>
</head>
<body></body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<title>Page</title>
</head>
<body></body>
</html>
字符编码
<!DOCTYPE html>
<html lang="en">
<head>
<title>Page</title>
<meta charset="UTF-8" />
</head>
<body></body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Page</title>
</head>
<body></body>
</html>
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(需要时)
<script>
if (!('fetch' in window)) {
document.write('<script src="/polyfills/fetch.js"><\/script>')
}
</script>
<script src="https://polyfill.io/v3/polyfill.min.js?features=fetch,IntersectionObserver"></script>
已弃用的 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');
}
事件监听器的 passive 选项
// ❌ 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 map
生产环境配置
// ❌ 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',
}
性能最佳实践
避免阻塞模式
// ❌ 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">
</div>
<ul>
<div>Item</div>
</ul>
<a href="/"><button>Click</button></a>
</div>
<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 标头
- 存在安全标头
- 没有暴露 Source map
兼容性
- 使用有效的 HTML5 doctype
- 在 head 中最先声明字符集
- 存在 Viewport meta 标签
- 未使用已弃用的 API
- 滚动/触摸事件使用 passive 监听器
代码质量
- 没有控制台错误
- HTML 有效(没有重复的 ID)
- 使用语义化 HTML 元素
- 正确处理错误
- 清理组件中的内存
用户体验
- 没有侵入式插页
- 在适当的场景请求权限
- 错误消息清晰
- 图片宽高比适当
工具
| 工具 | 用途 |
|---|---|
npm audit | 依赖项漏洞 |
| SecurityHeaders.com | 标头分析 |
| W3C Validator | HTML 验证 |
| Lighthouse | 最佳实践审计 |
| Observatory | 安全扫描 |
参考资料
评论
支持 Markdown