best-practices

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Best practices

最佳实践

Modern web development standards based on Lighthouse best practices audits. Covers security, browser compatibility, and code quality patterns.
基于Lighthouse最佳实践审计的现代Web开发标准,涵盖安全、浏览器兼容性和代码质量规范。

Security

安全

HTTPS everywhere

全面启用HTTPS

Enforce HTTPS:
html
<!-- ❌ Mixed content -->
<img src="http://example.com/image.jpg">
<script src="http://cdn.example.com/script.js"></script>

<!-- ✅ HTTPS only -->
<img src="https://example.com/image.jpg">
<script src="https://cdn.example.com/script.js"></script>

<!-- ✅ Protocol-relative (will use page's protocol) -->
<img src="//example.com/image.jpg">
HSTS Header:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
强制使用HTTPS:
html
<!-- ❌ Mixed content -->
<img src="http://example.com/image.jpg">
<script src="http://cdn.example.com/script.js"></script>

<!-- ✅ HTTPS only -->
<img src="https://example.com/image.jpg">
<script src="https://cdn.example.com/script.js"></script>

<!-- ✅ Protocol-relative (will use page's protocol) -->
<img src="//example.com/image.jpg">
HSTS 响应头:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

Content Security Policy (CSP)

内容安全策略(CSP)

html
<!-- Basic CSP via meta tag -->
<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;">

<!-- Better: HTTP header -->
CSP Header (recommended):
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';
Using nonces for inline scripts:
html
<script nonce="abc123">
  // This inline script is allowed
</script>
html
<!-- Basic CSP via meta tag -->
<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;">

<!-- Better: HTTP header -->
推荐的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):
html
<script nonce="abc123">
  // This inline script is allowed
</script>

Security headers

安全响应头

undefined
undefined

Prevent clickjacking

Prevent clickjacking

X-Frame-Options: DENY
X-Frame-Options: DENY

Prevent MIME type sniffing

Prevent MIME type sniffing

X-Content-Type-Options: nosniff
X-Content-Type-Options: nosniff

Enable XSS filter (legacy browsers)

Enable XSS filter (legacy browsers)

X-XSS-Protection: 1; mode=block
X-XSS-Protection: 1; mode=block

Control referrer information

Control referrer information

Referrer-Policy: strict-origin-when-cross-origin
Referrer-Policy: strict-origin-when-cross-origin

Permissions policy (formerly Feature-Policy)

Permissions policy (formerly Feature-Policy)

Permissions-Policy: geolocation=(), microphone=(), camera=()
undefined
Permissions-Policy: geolocation=(), microphone=(), camera=()
undefined

No vulnerable libraries

避免使用存在漏洞的库

bash
undefined
bash
undefined

Check for vulnerabilities

Check for vulnerabilities

npm audit yarn audit
npm audit yarn audit

Auto-fix when possible

Auto-fix when possible

npm audit fix
npm audit fix

Check specific package

Check specific package

npm ls lodash

**Keep dependencies updated:**
```json
// package.json
{
  "scripts": {
    "audit": "npm audit --audit-level=moderate",
    "update": "npm update && npm audit fix"
  }
}
Known vulnerable patterns to avoid:
javascript
// ❌ Prototype pollution vulnerable patterns
Object.assign(target, userInput);
_.merge(target, userInput);

// ✅ Safer alternatives
const safeData = JSON.parse(JSON.stringify(userInput));
npm ls lodash

**保持依赖更新:**
```json
// package.json
{
  "scripts": {
    "audit": "npm audit --audit-level=moderate",
    "update": "npm update && npm audit fix"
  }
}
需要避免的已知易受攻击模式:
javascript
// ❌ Prototype pollution vulnerable patterns
Object.assign(target, userInput);
_.merge(target, userInput);

// ✅ Safer alternatives
const safeData = JSON.parse(JSON.stringify(userInput));

Input sanitization

输入净化

javascript
// ❌ 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);
javascript
// ❌ XSS vulnerable
element.innerHTML = userInput;
document.write(userInput);

// ✅ 安全文本内容
element.textContent = userInput;

// ✅ 若需使用HTML,先净化
import DOMPurify from 'dompurify';
element.innerHTML = DOMPurify.sanitize(userInput);

Secure cookies

安全Cookie

javascript
// ❌ Insecure cookie
document.cookie = "session=abc123";

// ✅ Secure cookie (server-side)
Set-Cookie: session=abc123; Secure; HttpOnly; SameSite=Strict; Path=/

javascript
// ❌ 不安全的Cookie
document.cookie = "session=abc123";

// ✅ 安全Cookie(服务端设置)
Set-Cookie: session=abc123; Secure; HttpOnly; SameSite=Strict; Path=/

Browser compatibility

浏览器兼容性

Doctype declaration

文档类型声明

html
<!-- ❌ Missing or invalid doctype -->
<HTML>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">

<!-- ✅ HTML5 doctype -->
<!DOCTYPE html>
<html lang="en">
html
<!-- ❌ 缺失或无效的文档类型 -->
<HTML>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">

<!-- ✅ HTML5文档类型 -->
<!DOCTYPE html>
<html lang="en">

Character encoding

字符编码

html
<!-- ❌ Missing or late charset -->
<html>
<head>
  <title>Page</title>
  <meta charset="UTF-8">
</head>

<!-- ✅ Charset as first element in head -->
<html>
<head>
  <meta charset="UTF-8">
  <title>Page</title>
</head>
html
<!-- ❌ 缺失或晚声明的字符编码 -->
<html>
<head>
  <title>Page</title>
  <meta charset="UTF-8">
</head>

<!-- ✅ 在head最顶部声明字符编码 -->
<html>
<head>
  <meta charset="UTF-8">
  <title>Page</title>
</head>

Viewport meta tag

视口元标签

html
<!-- ❌ Missing viewport -->
<head>
  <title>Page</title>
</head>

<!-- ✅ Responsive viewport -->
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Page</title>
</head>
html
<!-- ❌ 缺失视口标签 -->
<head>
  <title>Page</title>
</head>

<!-- ✅ 响应式视口 -->
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Page</title>
</head>

Feature detection

特性检测

javascript
// ❌ 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;
  }
}
javascript
// ❌ 浏览器检测(不稳定)
if (navigator.userAgent.includes('Chrome')) {
  // 仅Chrome可用的代码
}

// ✅ 特性检测
if ('IntersectionObserver' in window) {
  // 使用IntersectionObserver
} else {
  // 降级方案
}

// ✅ 在CSS中使用@supports
@supports (display: grid) {
  .container {
    display: grid;
  }
}

@supports not (display: grid) {
  .container {
    display: flex;
  }
}

Polyfills (when needed)

必要时使用Polyfill

html
<!-- Load polyfills conditionally -->
<script>
  if (!('fetch' in window)) {
    document.write('<script src="/polyfills/fetch.js"><\/script>');
  }
</script>

<!-- Or use polyfill.io -->
<script src="https://polyfill.io/v3/polyfill.min.js?features=fetch,IntersectionObserver"></script>

html
<!-- 按需加载Polyfill -->
<script>
  if (!('fetch' in window)) {
    document.write('<script src="/polyfills/fetch.js"><\/script>');
  }
</script>

<!-- 或使用polyfill.io -->
<script src="https://polyfill.io/v3/polyfill.min.js?features=fetch,IntersectionObserver"></script>

Deprecated APIs

已废弃API

Avoid these

避免使用以下API

javascript
// ❌ 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');
}
javascript
// ❌ document.write(阻塞解析)
document.write('<script src="..."></script>');

// ✅ 动态加载脚本
const script = document.createElement('script');
script.src = '...';
document.head.appendChild(script);

// ❌ 同步XHR(阻塞主线程)
const xhr = new XMLHttpRequest();
xhr.open('GET', url, false); // false = 同步

// ✅ 异步fetch
const response = await fetch(url);

// ❌ Application Cache(已废弃)
<html manifest="cache.manifest">

// ✅ Service Workers
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/sw.js');
}

Event listener passive

被动事件监听器

javascript
// ❌ 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 });

javascript
// ❌ 非被动触摸/滚轮事件(可能阻塞滚动)
element.addEventListener('touchstart', handler);
element.addEventListener('wheel', handler);

// ✅ 被动监听器(支持流畅滚动)
element.addEventListener('touchstart', handler, { passive: true });
element.addEventListener('wheel', handler, { passive: true });

// ✅ 若需要preventDefault,显式声明
element.addEventListener('touchstart', handler, { passive: false });

Console & errors

控制台与错误处理

No console errors

避免控制台错误

javascript
// ❌ 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.');
}
javascript
// ❌ 生产环境中的错误
console.log('Debug info'); // 生产环境需移除
throw new Error('Unhandled'); // 捕获所有错误

// ✅ 正确的错误处理
try {
  riskyOperation();
} catch (error) {
  // 记录到错误追踪服务
  errorTracker.captureException(error);
  // 显示友好的用户提示
  showErrorMessage('出现错误,请重试。');
}

Error boundaries (React)

React错误边界

jsx
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>
jsx
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;
  }
}

// 使用方式
<ErrorBoundary>
  <App />
</ErrorBoundary>

Global error handler

全局错误处理器

javascript
// Catch unhandled errors
window.addEventListener('error', (event) => {
  errorTracker.captureException(event.error);
});

// Catch unhandled promise rejections
window.addEventListener('unhandledrejection', (event) => {
  errorTracker.captureException(event.reason);
});

javascript
// 捕获未处理的错误
window.addEventListener('error', (event) => {
  errorTracker.captureException(event.error);
});

// 捕获未处理的Promise拒绝
window.addEventListener('unhandledrejection', (event) => {
  errorTracker.captureException(event.reason);
});

Source maps

源映射

Production configuration

生产环境配置

javascript
// ❌ 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',
};

javascript
// ❌ 生产环境暴露源映射
// webpack.config.js
module.exports = {
  devtool: 'source-map', // 会暴露源代码
};

// ✅ 隐藏源映射(上传至错误追踪服务)
module.exports = {
  devtool: 'hidden-source-map',
};

// ✅ 或生产环境不生成源映射
module.exports = {
  devtool: process.env.NODE_ENV === 'production' ? false : 'source-map',
};

Performance best practices

性能最佳实践

Avoid blocking patterns

避免阻塞模式

javascript
// ❌ 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">
javascript
// ❌ 阻塞脚本
<script src="heavy-library.js"></script>

// ✅ 延迟脚本
<script defer src="heavy-library.js"></script>

// ❌ 阻塞CSS导入
@import url('other-styles.css');

// ✅ 链接标签(并行加载)
<link rel="stylesheet" href="styles.css">
<link rel="stylesheet" href="other-styles.css">

Efficient event handlers

高效事件处理器

javascript
// ❌ Handler on every element
items.forEach(item => {
  item.addEventListener('click', handleClick);
});

// ✅ Event delegation
container.addEventListener('click', (e) => {
  if (e.target.matches('.item')) {
    handleClick(e);
  }
});
javascript
// ❌ 为每个元素绑定处理器
items.forEach(item => {
  item.addEventListener('click', handleClick);
});

// ✅ 事件委托
container.addEventListener('click', (e) => {
  if (e.target.matches('.item')) {
    handleClick(e);
  }
});

Memory management

内存管理

javascript
// ❌ 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();

javascript
// ❌ 内存泄漏(未移除监听器)
const handler = () => { /* ... */ };
window.addEventListener('resize', handler);

// ✅ 完成后清理
const handler = () => { /* ... */ };
window.addEventListener('resize', handler);

// 组件卸载时执行清理:
window.removeEventListener('resize', handler);

// ✅ 使用AbortController
const controller = new AbortController();
window.addEventListener('resize', handler, { signal: controller.signal });

// 清理:
controller.abort();

Code quality

代码质量

Valid HTML

有效的HTML

html
<!-- ❌ Invalid HTML -->
<div id="header">
<div id="header"> <!-- Duplicate ID -->

<ul>
  <div>Item</div> <!-- Invalid child -->
</ul>

<a href="/"><button>Click</button></a> <!-- Invalid nesting -->

<!-- ✅ Valid HTML -->
<header id="site-header">
</header>

<ul>
  <li>Item</li>
</ul>

<a href="/" class="button">Click</a>
html
<!-- ❌ 无效HTML -->
<div id="header">
<div id="header"> <!-- 重复ID -->

<ul>
  <div>Item</div> <!-- 无效子元素 -->
</ul>

<a href="/"><button>Click</button></a> <!-- 无效嵌套 -->

<!-- ✅ 有效HTML -->
<header id="site-header">
</header>

<ul>
  <li>Item</li>
</ul>

<a href="/" class="button">Click</a>

Semantic HTML

语义化HTML

html
<!-- ❌ Non-semantic -->
<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>

<!-- ✅ Semantic HTML5 -->
<header>
  <nav>
    <a href="/">Home</a>
  </nav>
</header>
<main>
  <article>
    <h1>Headline</h1>
  </article>
</main>
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>

<!-- ✅ 语义化HTML5 -->
<header>
  <nav>
    <a href="/">Home</a>
  </nav>
</header>
<main>
  <article>
    <h1>Headline</h1>
  </article>
</main>

Image aspect ratios

图片宽高比

html
<!-- ❌ Distorted images -->
<img src="photo.jpg" width="300" height="100">
<!-- If actual ratio is 4:3, this squishes the image -->

<!-- ✅ Preserve aspect ratio -->
<img src="photo.jpg" width="300" height="225">
<!-- Actual 4:3 dimensions -->

<!-- ✅ CSS object-fit for flexibility -->
<img src="photo.jpg" style="width: 300px; height: 200px; object-fit: cover;">

html
<!-- ❌ 图片变形 -->
<img src="photo.jpg" width="300" height="100">
<!-- 若实际比例为4:3,会导致图片被压缩 -->

<!-- ✅ 保持宽高比 -->
<img src="photo.jpg" width="300" height="225">
<!-- 实际4:3尺寸 -->

<!-- ✅ 使用CSS object-fit实现灵活适配 -->
<img src="photo.jpg" style="width: 300px; height: 200px; object-fit: cover;">

Permissions & privacy

权限与隐私

Request permissions properly

合理请求权限

javascript
// ❌ 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);
  }
});
javascript
// ❌ 页面加载时请求(用户体验差,常被拒绝)
navigator.geolocation.getCurrentPosition(success, error);

// ✅ 用户操作后,在合适场景请求
findNearbyButton.addEventListener('click', async () => {
  // 说明权限用途
  if (await showPermissionExplanation()) {
    navigator.geolocation.getCurrentPosition(success, error);
  }
});

Permissions policy

权限策略

html
<!-- Restrict powerful features -->
<meta http-equiv="Permissions-Policy" 
      content="geolocation=(), camera=(), microphone=()">

<!-- Or allow for specific origins -->
<meta http-equiv="Permissions-Policy" 
      content="geolocation=(self 'https://maps.example.com')">

html
<!-- 限制高权限特性 -->
<meta http-equiv="Permissions-Policy" 
      content="geolocation=(), camera=(), microphone=()">

<!-- 或允许特定源使用 -->
<meta http-equiv="Permissions-Policy" 
      content="geolocation=(self 'https://maps.example.com')">

Audit checklist

审计检查清单

Security (critical)

安全(关键)

  • HTTPS enabled, no mixed content
  • No vulnerable dependencies (
    npm audit
    )
  • CSP headers configured
  • Security headers present
  • No exposed source maps
  • 已启用HTTPS,无混合内容
  • 无存在漏洞的依赖(
    npm audit
  • 已配置CSP响应头
  • 存在安全响应头
  • 未暴露源映射

Compatibility

兼容性

  • Valid HTML5 doctype
  • Charset declared first in head
  • Viewport meta tag present
  • No deprecated APIs used
  • Passive event listeners for scroll/touch
  • 有效的HTML5文档类型
  • 字符编码声明在head最顶部
  • 存在视口元标签
  • 未使用已废弃API
  • 为滚动/触摸事件使用被动监听器

Code quality

代码质量

  • No console errors
  • Valid HTML (no duplicate IDs)
  • Semantic HTML elements used
  • Proper error handling
  • Memory cleanup in components
  • 无控制台错误
  • 有效的HTML(无重复ID)
  • 使用了语义化HTML元素
  • 正确的错误处理
  • 组件中存在内存清理逻辑

UX

用户体验

  • No intrusive interstitials
  • Permission requests in context
  • Clear error messages
  • Appropriate image aspect ratios
  • 无侵入式插屏
  • 在合适场景请求权限
  • 清晰的错误提示
  • 图片宽高比合理

Tools

工具

ToolPurpose
npm audit
Dependency vulnerabilities
SecurityHeaders.comHeader analysis
W3C ValidatorHTML validation
LighthouseBest practices audit
ObservatorySecurity scan
工具用途
npm audit
检测依赖漏洞
SecurityHeaders.com响应头分析
W3C ValidatorHTML验证
Lighthouse最佳实践审计
Observatory安全扫描

References

参考资料