跳转至

前端最佳实践

📚 章节目标

本章节将全面介绍前端开发的各种最佳实践,包括代码规范、设计模式、性能优化等,帮助学习者掌握前端开发的核心方法。

学习目标

  1. 理解前端最佳实践的核心概念
  2. 掌握代码规范和约定
  3. 掌握常用设计模式
  4. 掌握性能优化技巧
  5. 理解可访问性最佳实践
  6. 掌握前端开发最佳实践

📝 代码规范

1. 命名规范

JavaScript
// 变量命名 - 驼峰命名法
const userName = 'John';
const isLoggedIn = true;
const maxCount = 100;

// 常量命名 - 全大写,下划线分隔
const API_URL = 'https://api.example.com';
const MAX_RETRY_COUNT = 3;
const DEFAULT_TIMEOUT = 5000;

// 函数命名 - 驼峰命名法,动词开头
function getUserById(id) {}
function calculateTotal(items) {}
function validateEmail(email) {}

// 类命名 - 驼峰命名法,首字母大写
class UserService {}
class UserRepository {}
class UserValidator {}

// 组件命名 - 驼峰命名法,首字母大写
function UserProfile() {}
function TodoList() {}
function SearchInput() {}

// 文件命名 - 驼峰命名法
// UserProfile.js
// TodoList.js
// SearchInput.js

// CSS类命名 - 短横线分隔
.user-profile {}
.todo-list {}
.search-input {}

// CSS变量命名 - 短横线分隔
:root {
  --primary-color: #007bff;
  --font-size-base: 16px;
  --spacing-unit: 8px;
}

2. 代码格式化

JavaScript
// 使用Prettier格式化代码
// .prettierrc.js
module.exports = {
  semi: true,
  singleQuote: true,
  tabWidth: 2,
  trailingComma: 'es5',
  printWidth: 80,
  arrowParens: 'always',
  endOfLine: 'lf',
};

// 不好的格式
const obj={name:"John",age:30};
function add(a,b){return a+b;}

// 好的格式
const obj = {
  name: 'John',
  age: 30,
};

function add(a, b) {
  return a + b;
}

3. 代码注释

JavaScript
// 单行注释 - 解释为什么,而不是是什么
// 使用缓存避免重复计算
const cachedValue = calculateExpensiveValue();

// 多行注释 - 解释复杂的逻辑
/**
 * 计算用户的折扣价格
 * @param {number} price - 原始价格
 * @param {Object} discount - 折扣对象
 * @param {string} discount.type - 折扣类型 ('percentage' | 'fixed')
 * @param {number} discount.value - 折扣值
 * @returns {number} - 折扣后的价格
 */
function calculateDiscount(price, discount) {
  if (discount.type === 'percentage') {
    return price * (1 - discount.value / 100);
  } else if (discount.type === 'fixed') {
    return price - discount.value;
  }
  return price;
}

// TODO注释 - 标记待办事项
// TODO: 实现用户认证功能
// FIXME: 修复这个bug
// HACK: 临时解决方案

🎨 设计模式

1. 单例模式

JavaScript
// 单例模式 - 确保只有一个实例
class Singleton {
  static instance = null;

  constructor() {
    if (Singleton.instance) {
      return Singleton.instance;
    }
    Singleton.instance = this;
  }

  static getInstance() {
    if (!Singleton.instance) {
      Singleton.instance = new Singleton();
    }
    return Singleton.instance;
  }
}

// 使用
const instance1 = Singleton.getInstance();
const instance2 = Singleton.getInstance();
console.log(instance1 === instance2); // true

2. 工厂模式

JavaScript
// 工厂模式 - 创建对象的接口
class ButtonFactory {
  static createButton(type) {
    switch (type) {
      case 'primary':
        return new PrimaryButton();
      case 'secondary':
        return new SecondaryButton();
      case 'danger':
        return new DangerButton();
      default:
        return new DefaultButton();
    }
  }
}

class PrimaryButton {
  render() {
    return <button className="btn btn-primary">Primary</button>;
  }
}

class SecondaryButton {
  render() {
    return <button className="btn btn-secondary">Secondary</button>;
  }
}

// 使用
const primaryButton = ButtonFactory.createButton('primary');
const secondaryButton = ButtonFactory.createButton('secondary');

3. 观察者模式

JavaScript
// 观察者模式 - 一对多依赖
class EventEmitter {
  constructor() {
    this.events = {};
  }

  on(event, callback) {
    if (!this.events[event]) {
      this.events[event] = [];
    }
    this.events[event].push(callback);
  }

  emit(event, data) {
    if (this.events[event]) {
      this.events[event].forEach(callback => callback(data));
    }
  }

  off(event, callback) {
    if (this.events[event]) {
      this.events[event] = this.events[event].filter(cb => cb !== callback);  // map转换每个元素;filter筛选;reduce累积
    }
  }
}

// 使用
const emitter = new EventEmitter();

emitter.on('user:login', (user) => {
  console.log('User logged in:', user);
});

emitter.emit('user:login', { name: 'John' });

4. 策略模式

JavaScript
// 策略模式 - 定义算法族,分别封装
class DiscountStrategy {
  calculate(price) {
    throw new Error('Must implement calculate method');
  }
}

class PercentageDiscount extends DiscountStrategy {
  constructor(percentage) {
    super();
    this.percentage = percentage;
  }

  calculate(price) {
    return price * (1 - this.percentage / 100);
  }
}

class FixedDiscount extends DiscountStrategy {
  constructor(amount) {
    super();
    this.amount = amount;
  }

  calculate(price) {
    return price - this.amount;
  }
}

// 使用
const percentageDiscount = new PercentageDiscount(10);
const fixedDiscount = new FixedDiscount(5);

console.log(percentageDiscount.calculate(100)); // 90
console.log(fixedDiscount.calculate(100)); // 95

⚡ 性能优化

1. 代码优化

JavaScript
// 避免不必要的渲染
// 不好的做法
function ExpensiveComponent({ data }) {
  const processedData = processData(data);
  return <div>{processedData}</div>;
}

// 好的做法
function ExpensiveComponent({ data }) {
  const processedData = useMemo(() => processData(data), [data]);
  return <div>{processedData}</div>;
}

// 避免内联函数
// 不好的做法
function ParentComponent() {
  const [count, setCount] = useState(0);  // 解构赋值:从对象/数组提取值

  return (
    <div>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <ChildComponent onClick={() => console.log('clicked')} />
    </div>
  );
}

// 好的做法
function ParentComponent() {
  const [count, setCount] = useState(0);

  const increment = useCallback(() => {
    setCount(prev => prev + 1);
  }, []);

  const handleClick = useCallback(() => {
    console.log('clicked');
  }, []);

  return (
    <div>
      <button onClick={increment}>Increment</button>
      <ChildComponent onClick={handleClick} />
    </div>
  );
}

2. 资源优化

JavaScript
// 图片优化
// 使用合适的格式
<img src="image.webp" alt="Description" />

// 使用响应式图片
<img
  src="image-small.jpg"
  srcset="
    image-small.jpg 400w,
    image-medium.jpg 800w,
    image-large.jpg 1200w
  "
  sizes="(max-width: 600px) 400px, 800px"
  alt="Description"
/>

// 懒加载图片
<img
  src="placeholder.jpg"
  data-src="actual-image.jpg"
  loading="lazy"
  alt="Description"
/>

// 字体优化
// 使用font-display
@font-face {
  font-family: 'CustomFont';
  src: url('font.woff2') format('woff2');
  font-display: swap;
}

// 预加载关键字体
<link
  rel="preload"
  href="/fonts/main.woff2"
  as="font"
  type="font/woff2"
  crossorigin
/>

3. 网络优化

JavaScript
// 代码分割
// 路由级代码分割
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));

// 组件级代码分割
const HeavyComponent = lazy(() => import('./HeavyComponent'));

// 预加载资源
<link rel="preload" href="/styles/main.css" as="style" />
<link rel="preload" href="/scripts/main.js" as="script" />
<link rel="preload" href="/fonts/main.woff2" as="font" />

// 预连接到重要域名
<link rel="preconnect" href="https://api.example.com" />
<link rel="dns-prefetch" href="https://cdn.example.com" />

♿ 可访问性

1. 语义化HTML

HTML
<!-- 不好的做法 -->
<div class="header">Header</div>
<div class="nav">Navigation</div>
<div class="main">Main Content</div>
<div class="footer">Footer</div>

<!-- 好的做法 -->
<header>Header</header>
<nav>Navigation</nav>
<main>Main Content</main>
<footer>Footer</footer>

2. ARIA属性

HTML
<!-- 按钮可访问性 -->
<button aria-label="Close dialog" onclick="closeDialog()">
  ×
</button>

<!-- 表单可访问性 -->
<label for="username">Username</label>
<input
  id="username"
  type="text"
  aria-required="true"
  aria-describedby="username-help"
/>
<small id="username-help">Enter your username</small>

<!-- 图标可访问性 -->
<button aria-label="Search">
  <svg aria-hidden="true">...</svg>
</button>

3. 键盘导航

JavaScript
// 键盘导航
function handleKeyDown(event) {
  switch (event.key) {
    case 'Enter':
    case ' ':
      handleClick();
      break;
    case 'Escape':
      handleClose();
      break;
    case 'ArrowUp':
      handlePrevious();
      break;
    case 'ArrowDown':
      handleNext();
      break;
  }
}

// 焦点管理
function Modal({ isOpen, onClose }) {
  const modalRef = useRef(null);

  useEffect(() => {
    if (isOpen && modalRef.current) {
      modalRef.current.focus();
    }
  }, [isOpen]);

  return (
    isOpen && (
      <div
        ref={modalRef}
        tabIndex={-1}
        onKeyDown={handleKeyDown}
        role="dialog"
        aria-modal="true"
      >
        <button onClick={onClose}>Close</button>
      </div>
    )
  );
}

📝 练习题

1. 基础题

题目1:实现单例模式

JavaScript
// 实现单例模式
class Singleton {
  // 实现单例逻辑
}

// 使用示例
const instance1 = Singleton.getInstance();
const instance2 = Singleton.getInstance();
console.log(instance1 === instance2); // true

2. 进阶题

题目2:实现观察者模式

JavaScript
// 实现观察者模式
class EventEmitter {
  // 实现事件监听和触发
}

// 使用示例
const emitter = new EventEmitter();  // const不可重新赋值;let块级作用域变量
emitter.on('event', (data) => console.log(data));  // 箭头函数:简洁的函数语法
emitter.emit('event', { message: 'Hello' });

3. 面试题

题目3:解释前端性能优化的策略

JavaScript
// 答案要点:
// 1. 代码优化
//    - 避免不必要的渲染
//    - 使用useMemo和useCallback
//    - 代码分割和懒加载

// 2. 资源优化
//    - 图片优化(格式、尺寸、懒加载)
//    - 字体优化(font-display、预加载)
//    - 资源压缩和缓存

// 3. 网络优化
//    - 使用CDN
//    - 启用HTTP/2
//    - 预加载和预连接

// 4. 渲染优化
//    - 虚拟列表
//    - 防抖和节流
//    - 避免同步布局

🎯 本章总结

本章节全面介绍了前端开发的各种最佳实践,包括代码规范、设计模式、性能优化、可访问性等。关键要点:

  1. 代码规范:掌握命名规范、代码格式化、代码注释
  2. 设计模式:掌握单例、工厂、观察者、策略等设计模式
  3. 性能优化:掌握代码优化、资源优化、网络优化
  4. 可访问性:掌握语义化HTML、ARIA属性、键盘导航
  5. 最佳实践:理解前端开发的各种最佳实践

下一步将深入学习前端面试准备。