目录


一、前端核心基石

1.1 JavaScript 核心

1.1.1 ES6+ 语法(生产环境必备)

【架构师视角】ES6+ 不是语法糖,是现代 JS 工程的基石

✅ 必须精通的特性
特性 生产场景 性能影响 安全考量
箭头函数 回调函数、组件定义、事件处理 无性能损耗 ⚠️ 避免 this 误用
解构赋值 Props 解构、API 响应处理、状态提取 微小优化 ✅ 减少临时变量
模板字符串 动态拼接 URL、HTML 片段、日志输出 无差异 ⚠️ XSS 风险需转义
Promise/async-await 异步数据请求、文件操作、定时任务 比 callback 更高效 ✅ 错误处理更清晰
模块化 (ESM) 组件拆分、工具函数、配置文件 Tree-shaking 友好 ✅ 作用域隔离
🎯 实战代码示例
// ❌ 反面教材:回调地狱 + this 问题
class OldComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { data: null };
    // 必须手动绑定 this
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    fetch('/api/data')
      .then(response => response.json())
      .then(data => {
        this.setState({ data });
      })
      .catch(error => console.error(error));
  }
}

// ✅ 最佳实践:箭头函数 + async/await + 解构
const ModernComponent = ({ userId }) => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  const fetchData = useCallback(async () => {
    setLoading(true);
    setError(null);

    try {
      const response = await fetch(`/api/users/${userId}`);
      if (!response.ok) throw new Error('Network error');

      const { user, permissions } = await response.json();
      setData(user);
    } catch (err) {
      setError(err.message);
      // 生产环境应接入错误监控(如 Sentry)
      console.error('[API Error]', err);
    } finally {
      setLoading(false);
    }
  }, [userId]);

  useEffect(() => {
    fetchData();
  }, [fetchData]);

  return (
    <div className="user-profile">
      {loading && <Skeleton />}
      {error && <ErrorAlert message={error} />}
      {data && <UserCard {...data} />}
    </div>
  );
};
🔒 安全性要点
// ⚠️ 危险:模板字符串直接插入用户输入(XSS 风险)
const BadExample = ({ userInput }) => (
  <div dangerouslySetInnerHTML={{ __html: `${userInput}` }} />
);

// ✅ 安全:React 自动转义 + 显式验证
const SafeExample = ({ userInput }) => {
  const sanitizedInput = DOMPurify.sanitize(userInput); // 使用消毒库
  return <div>{sanitizedInput}</div>; // React 默认转义
};
1.1.2 闭包(Closure)—— React Hooks 的底层原理

【架构师洞察】不理解闭包,就无法真正掌握 useEffect  useState

📚 核心概念
// 闭包的本质:函数记住并访问其词法作用域
function createCounter() {
  let count = 0; // 私有变量,外部无法直接访问

  return {
    increment: () => ++count,     // 闭包引用 count
    decrement: () => --count,
    getCount: () => count,
    reset: () => { count = 0; }
  };
}

// 应用到 React State
const Counter = () => {
  const [count, setCount] = useState(0); // useState 内部就是闭包实现

  // 每次 render 都会创建新的闭包
  const handleIncrement = () => {
    setCount(prev => prev + 1); // 函数式更新,避免闭包陷阱
  };

  return <button onClick={handleIncrement}>{count}</button>;
};
⚠️ 经典闭包陷阱(Stale Closure)
// ❌ 错误示例:闭包捕获了旧的 state
const TimerBad = () => {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const id = setInterval(() => {
      setCount(count + 1); // count 永远是 0!闭包陷阱
    }, 1000);

    return () => clearInterval(id);
  }, []); // 空依赖导致闭包过期

  return <div>{count}</div>;
};

// ✅ 正确方案 1:使用函数式更新
const TimerGood1 = () => {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const id = setInterval(() => {
      setCount(c => c + 1); // 接收最新 state
    }, 1000);
    return () => clearInterval(id);
  }, []);

  return <div>{count}</div>;
};

// ✅ 正确方案 2:使用 useRef 保持可变引用
const TimerGood2 = () => {
  const [count, setCount] = useState(0);
  const countRef = useRef(count);

  useEffect(() => {
    const id = setInterval(() => {
      countRef.current += 1;
      setCount(countRef.current);
    }, 1000);
    return () => clearInterval(id);
  }, []);

  return <div>{count}</div>;
};
1.1.3 原型链与继承(理解 React 类组件)

【现代视角】虽然函数组件是主流,但理解原型链有助于阅读 legacy 代码

// React 类组件的继承机制
class MyComponent extends React.Component {
  constructor(props) {
    super(props); // 必须调用父类构造器
    this.state = { value: props.initialValue };
  }

  // 方法挂载在原型上(所有实例共享)
  handleClick() {
    this.setState(prev => ({ value: prev.value + 1 }));
  }

  render() {
    // render 是 React 触发的,this 已绑定
    return (
      <button onClick={() => this.handleClick()}>
        {this.state.value}
      </button>
    );
  }
}

// 原型链示意图:
// MyComponent 实例 → MyComponent.prototype → Component.prototype → Object.prototype
1.1.4 异步编程(Event Loop 与微任务/宏任务)

【性能关键】理解 Event Loop 才能优化渲染性能

┌─────────────────────────────────────┐
│           Call Stack(调用栈)        │
│  ┌─────────────────────────────┐   │
│  │  render() → setState()      │   │ ← 同步代码
│  └─────────────────────────────┘   │
└──────────────┬──────────────────────┘
               │
               ▼
┌─────────────────────────────────────┐
│       Web APIs(异步操作)            │
│  • setTimeout / setInterval          │
│  • fetch / XMLHttpRequest            │
│  • DOM 事件                          │
│  • Promise.then() → Microtask Queue │
└──────────────┬──────────────────────┘
               │
               ▼
┌─────────────────────────────────────┐
│     Task Queues(任务队列)          │
│  ┌─────────────┐  ┌──────────────┐ │
│  │ Microtask   │  │ Macrotask    │ │
│  │ (Promise)   │  │ (setTimeout) │ │
│  │ 优先级更高   │  │              │ │
│  └─────────────┘  └──────────────┘ │
└─────────────────────────────────────┘
               │
               ▼
         Event Loop(事件循环)
🎯 React 渲染中的异步应用
// React 18 的 Concurrent Mode 利用 microtask 优先级
const App = () => {
  const [isPending, startTransition] = useTransition();
  const [searchTerm, setSearchTerm] = useState('');

  const handleSearch = (value) => {
    // 紧急更新:立即响应用户输入
    setSearchTerm(value);

    // 非紧急更新:降低优先级,避免阻塞交互
    startTransition(() => {
      performExpensiveSearch(value);
    });
  };

  return <SearchInput onChange={handleSearch} isLoading={isPending} />;
};

1.2 CSS 基础与工程化

1.2.1 现代 CSS 布局体系

【架构师建议】Flexbox 用于一维布局,Grid 用于二维布局,避免滥用 absolute 定位

Flexbox(弹性盒子)—— 一维布局之王
/* 经典的 Header 布局 */
.header {
  display: flex;
  justify-content: space-between; /* 主轴对齐 */
  align-items: center;            /* 交叉轴对齐 */
  padding: 0 24px;
  height: 64px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}

.logo {
  flex-shrink: 0; /* 防止 Logo 被压缩 */
}

.nav-menu {
  display: flex;
  gap: 32px; /* 现代间距方案,替代 margin */
}

.user-actions {
  margin-left: auto; /* 推到右侧 */
}
Grid(网格布局)—— 二维布局利器
/* 后台管理系统经典布局 */
.dashboard {
  display: grid;
  grid-template-columns: 240px 1fr; /* 侧边栏 + 内容区 */
  grid-template-rows: auto 1fr auto;
  grid-template-areas:
    "header header"
    "sidebar main"
    "sidebar footer";
  min-height: 100vh;
}

.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }
.footer { grid-area: footer; }

/* 响应式适配 */
@media (max-width: 768px) {
  .dashboard {
    grid-template-columns: 1fr;
    grid-template-areas:
      "header"
      "main"
      "footer";
  }
  .sidebar { display: none; } /* 或改为抽屉模式 */
}
1.2.2 BEM 命名规范(Block Element Modifier)

【团队协作标准】BEM 解决 CSS 命名冲突,提升可维护性

/* Block:独立的功能块 */
.card { }

/* Element:块的组成部分 */
.card__title { }
.card__content { }
.card__footer { }

/* Modifier:状态或变体 */
.card--featured { }
.card--disabled { }
.card__title--large { }
// React 中配合 CSS Modules 使用
import styles from './Card.module.css';

const Card = ({ isFeatured, title, children }) => (
  <div className={`${styles.card} ${isFeatured ? styles['card--featured'] : ''}`}>
    <h3 className={styles.card__title}>{title}</h3>
    <div className={styles.card__content}>{children}</div>
  </div>
);
1.2.3 响应式设计策略

【移动优先】从 Mobile First 到 Progressive Enhancement

/* 移动优先:默认样式针对小屏 */
.container {
  width: 100%;
  padding: 16px;
}

/* 平板设备 */
@media (min-width: 768px) {
  .container {
    max-width: 720px;
    margin: 0 auto;
    padding: 24px;
  }
}

/* 桌面设备 */
@media (min-width: 1024px) {
  .container {
    max-width: 1200px;
    padding: 32px;
  }
}

/* 大屏设备 */
@media (min-width: 1440px) {
  .container {
    max-width: 1400px;
  }
}
🎯 React 中的响应式方案对比
方案 适用场景 性能 可维护性
CSS Media Queries 简单断点切换 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐
CSS-in-JS (styled-components) 动态主题、复杂逻辑 ⭐⭐⭐ ⭐⭐⭐⭐⭐
Tailwind CSS 快速原型、原子化开发 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐
CSS Modules + PostCSS 中大型项目 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐

1.3 浏览器原理与网络

1.3.1 浏览器渲染流程(React Virtual DOM 的存在意义)
用户操作 → JavaScript → Virtual DOM → Diff Algorithm → Real DOM Update
                                        ↓
                              [Batching & Reconciliation]
                                        ↓
                    Layout → Paint → Composite → Display
🎯 关键渲染路径优化
// ❌ 强制同步布局(Layout Thrashing)
function badPerformance() {
  const elements = document.querySelectorAll('.item');
  elements.forEach(el => {
    const height = el.offsetHeight; // 读取 → 触发 reflow
    el.style.height = `${height * 2}px`; // 写入 → 再次触发 reflow
  }); // 循环 n 次 = n 次 reflow
}

// ✅ 批量读写(React 自动优化这一点)
function goodPerformance() {
  const elements = document.querySelectorAll('.item');
  // 第一轮:批量读取
  const heights = Array.from(el => el.offsetHeight);
  // 第二轮:批量写入
  elements.forEach((el, i) => {
    el.style.height = `${heights[i] * 2}px`;
  }); // 只触发 2 次 reflow
}
1.3.2 跨域解决方案(CORS 配置)

【安全第一】跨域不是 bug,是浏览器安全机制

// 前端请求配置
const apiClient = axios.create({
  baseURL: process.env.REACT_APP_API_BASE_URL,
  timeout: 10000,
  withCredentials: true, // 搭带 Cookie(需要后端配合)
  headers: {
    'Content-Type': 'application/json',
    'X-Requested-With': 'XMLHttpRequest',
  },
});

// 请求拦截器:添加认证 Token
apiClient.interceptors.request.use(config => {
  const token = localStorage.getItem('access_token');
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

// 响应拦截器:统一错误处理
apiClient.interceptors.response.use(
  response => response.data,
  error => {
    if (error.response?.status === 401) {
      // Token 过期,跳转登录
      window.location.href = '/login';
    }
    return Promise.reject(error);
  }
);
🔒 CORS 安全配置(服务端)
# Nginx 配置示例
location /api/ {
    # 仅允许指定域名跨域(禁止 *)
    add_header Access-Control-Allow-Origin https://yourdomain.com always;

    # 允许的请求方法
    add_header Access-Control-Allow-Methods 'GET, POST, PUT, DELETE, OPTIONS' always;

    # 允许的请求头
    add_header Access-Control-Allow-Headers 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization' always;

    # 允许携带凭证
    add_header Access-Control-Allow-Credentials true always;

    # 预检请求缓存时间(秒)
    add_header Access-Control-Max-Age 1728000 always;

    # 处理 OPTIONS 预检请求
    if ($request_method = 'OPTIONS') {
        return 204;
    }

    proxy_pass http://backend_server;
}
1.3.3 本地存储方案选型
存储方式 容量 持久性 安全等级 适用场景
Cookie 4KB 可设置过期 ⚠️ 易被窃取 Session ID、CSRF Token
LocalStorage 5-10MB 永久(除非清除) ⚠️ 明文存储 用户偏好设置、非敏感缓存
SessionStorage 5-10MB 关闭标签页即清除 ⚠️ 明文存储 表单临时数据、页面状态
IndexedDB 250MB+ 永久 ✅ 可加密 离线存储、大型数据缓存
🔒 安全存储实践
// ✅ 敏感数据加密存储(使用 crypto-js)
import CryptoJS from 'crypto-js';

const SECRET_KEY = process.env.REACT_APP_ENCRYPTION_KEY;

const secureStorage = {
  setItem(key, value) {
    const encrypted = CryptoJS.AES.encrypt(JSON.stringify(value), SECRET_KEY).toString();
    localStorage.setItem(key, encrypted);
  },

  getItem(key) {
    const encrypted = localStorage.getItem(key);
    if (!encrypted) return null;

    try {
      const bytes = CryptoJS.AES.decrypt(encrypted, SECRET_KEY);
      return JSON.parse(bytes.toString(CryptoJS.enc.Utf8));
    } catch {
      console.error('Decryption failed');
      return null;
    }
  },

  removeItem(key) {
    localStorage.removeItem(key);
  }
};

// 使用示例
secureStorage.setItem('user_token', { token: 'xxx', expires: Date.now() + 3600000 });
const userData = secureStorage.getItem('user_token');

1.4 开发工具链

1.4.1 Git 工作流(团队协作基础)

【业界标准】Git Flow 或 GitHub Flow,杜绝直接 push main

main (生产环境)
  ├── develop (开发分支)
  │     ├── feature/user-login (功能分支)
  │     ├── feature/payment-system
  │     └── hotfix/security-patch (紧急修复)
  └── release/v1.2.0 (发布分支)
📝 Git 命令速查
# 日常开发流程
git checkout -b feature/new-feature    # 创建功能分支
git add .                               # 暂存更改
git commit -m "feat: add user login"    # 提交(遵循 Conventional Commits)
git push origin feature/new-feature     # 推送远程
git pull request                        # 创建 PR 进行 Code Review

# 合并代码
git checkout develop
git merge --no-ff feature/new-feature   # 保留合并历史
git branch -d feature/new-feature       # 删除本地分支

# 紧急修复
git checkout main
git checkout -b hotfix/critical-bug
# ... 修复 ...
git merge --no-ff hotfix/critical-bug
git tag -a v1.2.1 -m "Release version 1.2.1"
1.4.2 Chrome DevTools 调试技巧

【效率提升】掌握这些技巧,调试效率翻倍

🎯 React 专用调试
1. Components 面板:
   - 查看 Props/State/Hooks 实时值
   - 直接修改 State 测试 UI 变化
   - 追踪组件渲染原因(为什么重新渲染?)

2. Profiler 面板:
   - 录制渲染性能
   - 识别未优化的组件(红色标记)
   - 分析渲染耗时分布

3. Console 面板:
   - $r → 当前选中的 React 组件实例
   - $0 → 当前选中的 DOM 元素
💡 高效调试命令
// 在控制台中使用
console.table(users);                  // 表格化输出对象数组
console.group('API Request');          // 分组日志
console.log('URL:', url);
console.log('Params:', params);
console.groupEnd();

console.time('Fetch Data');            // 计时器
fetch('/api/data').then(() => {
  console.timeEnd('Fetch Data');       // 输出耗时
});

// 条件断点(在 Sources 面板右键行号)
// 只有当条件满足时才暂停
users.length > 0

二、React 核心基础 API

2.1 核心概念深度解析

2.1.1 JSX 语法本质(Syntactic Sugar)

【底层原理】JSX 只是 React.createElement() 的语法糖

// 你写的 JSX
const element = <h1 className="greeting">Hello, world!</h1>;

// Babel 编译后的真实代码
const element = React.createElement(
  'h1',
  { className: 'greeting' },
  'Hello, world!'
);

// 最终生成的 JavaScript 对象(Virtual DOM)
const element = {
  type: 'h1',
  props: {
    className: 'greeting',
    children: 'Hello, world!'
  },
  key: null,
  ref: null
};
🎯 JSX 使用规范
// ✅ 最佳实践
const UserProfile = ({ user, onEdit }) => (
  <article className="user-card">
    <header>
      <img
        src={user.avatar}
        alt={`${user.name}的头像`} // 动态 alt 文本(无障碍)
        loading="lazy"             // 图片懒加载(性能优化)
      />
      <h2>{user.name}</h2>
    </header>

    <section className="user-info">
      <p><strong>邮箱:</strong>{user.email}</p>
      <p><strong>角色:</strong>{user.role}</p>
    </section>

    <footer>
      <button onClick={() => onEdit(user.id)}>
        编辑资料
      </button>
    </footer>
  </article>
);

// ❌ 避免的反模式
const BadExample = () => (
  <div>
    {/* 不要在 JSX 中写复杂逻辑 */}
    {items.map(item => {
      if (item.type === 'special') {
        return <SpecialItem key={item.id} {...item} />;
      } else if (item.type === 'normal') {
        return <NormalItem key={item.id} {...item} />;
      }
      // 缺少 fallback,可能导致 undefined 渲染
    })}
  </div>
);
2.1.2 虚拟 DOM 与 Diff 算法

【性能核心】理解 Virtual DOM 才能写出高性能组件

🔄 Diff 算法的三个假设(O(n) 复杂度的基础)
  1. 不同类型的元素 → 不同的树
    // 完全替换,不比较子节点
    <div>...</div> → <span>...</span>
    
  2. 通过 key 标识同级子元素
    // ✅ 使用稳定的唯一标识
    <TodoList items={todos.map(todo => (
      <TodoItem key={todo.id} {...todo} />
    ))} />
    
    // ❌ 使用索引作为 key(反模式)
    <TodoList items={todos.map((todo, index) => (
      <TodoItem key={index} {...todo} /> // 插入/删除会导致错误复用
    ))} />
    
  3. 只同层比较,不跨层
    // 不会跨层级 diff
    <div>
      <A />
      <B />  ← 只和原来的 B 比较
    </div>
    
📊 Diff 算法实战演示
// 初始状态
const oldVDOM = ['A', 'B', 'C', 'D'];

// 新状态:在头部插入 E
const newVDOM = ['E', 'A', 'B', 'C', 'D'];

// ❌ 如果 key 用索引(0,1,2,3,4):
// A(0→1), B(1→2), C(2→3), D(3→4), 新增 E(0)
// 结果:4 次更新 + 1 次新增 = 5 次 DOM 操作

// ✅ 如果 key 用唯一 id(id_A, id_B, ...):
// 新增 E(id_E),其余保持不变
// 结果:1 次新增 = 1 次 DOM 操作(性能提升 5 倍!)
2.1.3 函数组件 vs 类组件(2026 年的选择)

【架构决策】函数组件 + Hooks 是绝对主流,类组件仅用于维护 Legacy 代码

维度 函数组件(推荐) 类组件(Legacy)
代码量 少 30-50% 冗长
this 指向 无此问题 容易出错
逻辑复用 自定义 Hooks HOC / Render Props
Tree Shaking ✅ 支持 ❌ 不支持
性能 相同(React 内部优化) 相同
未来支持 ✅ 持续迭代 ❌ 维护模式
🔄 迁移指南(类组件 → 函数组件)
// ❌ 旧版类组件
class CounterClass extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
    this.increment = this.increment.bind(this);
  }

  increment() {
    this.setState(({ count }) => ({ count: count + 1 }));
  }

  componentDidMount() {
    document.title = `Count: ${this.state.count}`;
  }

  componentDidUpdate(prevProps, prevState) {
    if (prevState.count !== this.state.count) {
      document.title = `Count: ${this.state.count}`;
    }
  }

  render() {
    return (
      <button onClick={this.increment}>
        Count: {this.state.count}
      </button>
    );
  }
}

// ✅ 现代函数组件
const CounterFunction = () => {
  const [count, setCount] = useState(0);

  // componentDidMount + componentDidUpdate 合并
  useEffect(() => {
    document.title = `Count: ${count}`;
  }, [count]); // 依赖数组明确声明

  const increment = useCallback(() => {
    setCount(c => c + 1);
  }, []); // 箭头函数无需绑定 this

  return (
    <button onClick={increment}>
      Count: {count}
    </button>
  );
};

2.2 渲染逻辑最佳实践

2.2.1 条件渲染(Conditional Rendering)

【可读性优先】选择合适的条件渲染方式

🎯 场景化方案选择
// 1. 三元运算符:简单的二选一场景
const StatusBadge = ({ status }) => (
  <span className={`badge badge--${status}`}>
    {status === 'active' ? '启用' : '禁用'}
  </span>
);

// 2. 逻辑与 (&&):可选渲染(注意 falsy 值陷阱)
const UserGreeting = ({ user }) => (
  <div>
    {user && <WelcomeMessage name={user.name} />}  // ⚠️ user=0 时不会渲染
    {!!user && <WelcomeMessage name={user.name} />} // ✅ 显式转为布尔值
  </div>
);

// 3. 立即执行函数:复杂条件逻辑(避免过度嵌套)
const ComplexCondition = ({ userType, isLoggedIn, hasPermission }) => (
  <div>
    {(() => {
      if (!isLoggedIn) return <LoginPrompt />;
      if (userType === 'admin') return <AdminPanel />;
      if (hasPermission) return <UserDashboard />;
      return <AccessDenied />;
    })()}
  </div>
);

// 4. 抽取为独立组件(最推荐):复杂渲染逻辑
const ContentSwitcher = ({ userType, isLoggedIn, hasPermission }) => {
  if (!isLoggedIn) return <LoginPrompt />;
  if (userType === 'admin') return <AdminPanel />;
  if (hasPermission) return <UserDashboard />;
  return <AccessDenied />;
};

const App = () => (
  <Layout>
    <ContentSwitcher {...props} />
  </Layout>
);
⚠️ 常见陷阱
// ❌ 陷阱 1:数字 0 被视为 falsy
const ItemCount = ({ count }) => (
  <div>
    {count && <span>共 {count} 项</span>}  {/* count=0 时不显示 */}
  </div>
);

// ✅ 修复方案
const ItemCountFixed = ({ count }) => (
  <div>
    {count > 0 && <span>共 {count} 项</span>}
    {count === 0 && <span>暂无数据</span>}
  </div>
);

// ❌ 陷阱 2:三元运算符嵌套过深
const BadNested = ({ status, role, isNew }) => (
  <div>
    {status === 'loading'
      ? <Spinner />
      : status === 'error'
        ? <Error />
        : role === 'admin'
          ? <Admin />
          : isNew
            ? <NewUser />
            : <NormalUser />
    }
  </div>
);

// ✅ 修复:提前返回或使用对象映射
const ComponentMap = {
  loading: Spinner,
  error: Error,
  admin: Admin,
  newUser: NewUser,
  default: NormalUser,
};

const GoodMapping = ({ status, role, isNew }) => {
  const getComponentKey = () => {
    if (status === 'loading') return 'loading';
    if (status === 'error') return 'error';
    if (role === 'admin') return 'admin';
    if (isNew) return 'newUser';
    return 'default';
  };

  const Component = ComponentMap[getComponentKey()];
  return <Component />;
};
2.2.2 列表渲染与 Key 原理

【性能关键】key 是 React Diff 算法的核心优化点

🎯 Key 的三大原则
  1. 稳定性:同一数据在不同渲染中 key 不变
  2. 唯一性:兄弟元素之间 key 不能重复
  3. 可预测性:key 应该来自数据本身,而非随机生成
// ✅ 最佳实践:使用业务主键
const TodoList = ({ todos }) => (
  <ul>
    {todos.map(todo => (
      <li key={todo.id}>  {/* 数据库主键或唯一标识 */}
        {todo.text}
      </li>
    ))}
  </ul>
);

// ⚠️ 可接受:组合字段作为 key(当没有单一主键时)
const UserList = ({ users }) => (
  <div>
    {users.map(user => (
      <UserProfile
        key={`${user.departmentId}-${user.employeeId}`}  // 组合唯一键
        {...user}
      />
    ))}
  </div>
);

// ❌ 绝对禁止:使用 Math.random()
const TerribleExample = ({ items }) => (
  <ul>
    {items.map(item => (
      <li key={Math.random()}>  {/* 每次渲染都生成新 key! */}
        {item.name}
      </li>
    ))}
  </ul>
);

// ❌ 避免使用:数组索引(仅在静态列表中可用)
const StaticNav = () => (
  <nav>
    {['首页', '产品', '关于我们'].map((text, index) => (
      <a key={index} href="#">{text}</a>  {/* 静态列表可以接受 */}
    ))}
  </nav>
);
📊 Key 对 Diff 算法的影响可视化
// 场景:待办事项列表,在中间插入新项目

// 初始状态
const before = [
  { id: 'a', text: '学习 React' },
  { id: 'b', text: '学习 Vue' },
  { id: 'c', text: '学习 Angular' },
];

// 操作:在位置 0 插入新项目
const after = [
  { id: 'x', text: '学习 HTML' },  // 新增
  { id: 'a', text: '学习 React' },
  { id: 'b', text: '学习 Vue' },
  { id: 'c', text: '学习 Angular' },
];

// 方案 1:使用索引 key (0, 1, 2 → 0, 1, 2, 3)
// Diff 结果:
// - key=0: text '学习 React' → '学习 HTML' (更新)
// - key=1: text '学习 Vue' → '学习 React' (更新)
// - key=2: text '学习 Angular' → '学习 Vue' (更新)
// - 新增 key=3: '学习 Angular'
// 总计:3 次更新 + 1 次新增 = 4 次 DOM 操作 ❌

// 方案 2:使用 id key ('a', 'b', 'c' → 'x', 'a', 'b', 'c')
// Diff 结果:
// - 新增 key='x': '学习 HTML'
// - key='a', 'b', 'c': 内容未变,保留原位
// 总计:1 次新增 = 1 次 DOM 操作 ✅ (性能提升 400%!)
2.2.3 表单受控组件与非受控组件

【表单处理】受控组件是 React 的推荐做法

🎯 受控组件(Controlled Components)
// ✅ 推荐:完全由 React state 控制的表单
const RegistrationForm = () => {
  const [formData, setFormData] = useState({
    username: '',
    email: '',
    password: '',
    confirmPassword: '',
    agreeTerms: false,
  });

  const [errors, setErrors] = useState({});
  const [touched, setTouched] = useState({});

  // 统一的变更处理器
  const handleChange = (field) => (e) => {
    const { value, type, checked } = e.target;
    const newValue = type === 'checkbox' ? checked : value;

    setFormData(prev => ({
      ...prev,
      [field]: newValue,
    }));

    // 实时校验(用户输入时)
    if (touched[field]) {
      validateField(field, newValue);
    }
  };

  // 失去焦点时标记为已触摸
  const handleBlur = (field) => () => {
    setTouched(prev => ({ ...prev, [field]: true }));
    validateField(field, formData[field]);
  };

  // 字段校验逻辑
  const validateField = (field, value) => {
    let error = '';
    switch (field) {
      case 'username':
        if (!value) error = '用户名不能为空';
        else if (value.length < 3) error = '用户名至少 3 个字符';
        break;
      case 'email':
        if (!value) error = '邮箱不能为空';
        else if (!/\S+@\S+\.\S+/.test(value)) error = '邮箱格式不正确';
        break;
      case 'password':
        if (!value) error = '密码不能为空';
        else if (value.length < 8) error = '密码至少 8 个字符';
        break;
      case 'confirmPassword':
        if (value !== formData.password) error = '两次密码不一致';
        break;
    }
    setErrors(prev => ({ ...prev, [field]: error }));
    return !error;
  };

  // 提交处理
  const handleSubmit = async (e) => {
    e.preventDefault();

    // 标记所有字段为已触摸
    const allFields = Object.keys(formData);
    setTouched(allFields.reduce((acc, field) => ({ ...acc, [field]: true }), {}));

    // 校验所有字段
    const isValid = allFields.every(field => validateField(field, formData[field]));

    if (!isValid || !formData.agreeTerms) {
      alert('请检查表单填写是否正确');
      return;
    }

    try {
      await api.register(formData);
      toast.success('注册成功!');
      // 跳转到登录页...
    } catch (err) {
      toast.error(err.message);
    }
  };

  return (
    <form onSubmit={handleSubmit} className="registration-form">
      <FormField
        label="用户名"
        required
        error={touched.username && errors.username}
      >
        <input
          type="text"
          value={formData.username}
          onChange={handleChange('username')}
          onBlur={handleBlur('username')}
          placeholder="请输入用户名"
          autoComplete="username"
        />
      </FormField>

      <FormField
        label="邮箱"
        required
        error={touched.email && errors.email}
      >
        <input
          type="email"
          value={formData.email}
          onChange={handleChange('email')}
          onBlur={handleBlur('email')}
          placeholder="example@email.com"
          autoComplete="email"
        />
      </FormField>

      <FormField
        label="密码"
        required
        error={touched.password && errors.password}
      >
        <input
          type="password"
          value={formData.password}
          onChange={handleChange('password')}
          onBlur={handleBlur('password')}
          placeholder="至少 8 个字符"
          autoComplete="new-password"
        />
      </FormField>

      <label className="checkbox-label">
        <input
          type="checkbox"
          checked={formData.agreeTerms}
          onChange={handleChange('agreeTerms')}
        />
        我已阅读并同意《用户协议》
      </label>

      <button type="submit" disabled={!formData.agreeTerms}>
        注册
      </button>
    </form>
  );
};
🎯 非受控组件(Uncontrolled Components)适用场景
// 适用场景:文件上传、一次性表单、集成第三方库
const FileUploadForm = () => {
  const fileInputRef = useRef(null);

  const handleSubmit = (e) => {
    e.preventDefault();
    // 通过 ref 访问 DOM 元素获取值(不需要 state)
    const file = fileInputRef.current?.files[0];
    if (file) {
      uploadFile(file);
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      {/* 非受控:不绑定 value/state */}
      <input
        ref={fileInputRef}
        type="file"
        accept=".pdf,.doc,.docx"
      />
      <button type="submit">上传文件</button>
    </form>
  );
};

2.3 组件生命周期管理

2.3.1 类组件生命周期(了解即可,用于阅读旧代码)
挂载阶段 (Mounting):
  constructor() → static getDerivedStateFromProps() → render() → componentDidMount()

更新阶段 (Updating):
  static getDerivedStateFromProps() → shouldComponentUpdate() → render() → getSnapshotBeforeUpdate() → componentDidUpdate()

卸载阶段 (Unmounting):
  componentWillUnmount()

错误处理 (Error Handling):
  static getDerivedStateFromError() → componentDidCatch()
📖 各阶段典型用途
class LegacyComponent extends React.Component {
  constructor(props) {
    super(props);
    // 1. 初始化 state
    // 2. 绑定方法 this
    this.state = { data: null, loading: false };
  }

  // 3. 从 props 派生 state(少用,容易导致冗余 state)
  static getDerivedStateFromProps(nextProps, prevState) {
    if (nextProps.userId !== prevState.userId) {
      return { data: null }; // 重置数据
    }
    return null; // 不更新 state
  }

  // 4. 性能优化:决定是否重新渲染(慎用!)
  shouldComponentUpdate(nextProps, nextState) {
    // 仅当特定 props/state 变化时才更新
    return nextProps.id !== this.props.id ||
           nextState.loading !== this.state.loading;
  }

  // 5. DOM 挂载完成后:副作用操作(网络请求、订阅、DOM 操作)
  componentDidMount() {
    this.fetchData();
    this.timer = setInterval(this.updateTime, 1000);
    document.addEventListener('resize', this.handleResize);
  }

  // 6. DOM 更新前:获取快照(用于滚动位置恢复等)
  getSnapshotBeforeUpdate(prevProps, prevState) {
    if (prevProps.list.length < this.props.list.length) {
      const list = this.listRef.current;
      return list.scrollHeight - list.scrollTop; // 返回滚动偏移
    }
    return null;
  }

  // 7. DOM 更新完成后:DOM 操作、网络请求(基于 props 变化)
  componentDidUpdate(prevProps, prevState, snapshot) {
    if (snapshot !== null) {
      this.listRef.current.scrollTop =
        this.listRef.current.scrollHeight - snapshot;
    }
  }

  // 8. 组件卸载前:清理工作(取消订阅、清除定时器)
  componentWillUnmount() {
    clearInterval(this.timer);
    document.removeEventListener('resize', this.handleResize);
    this.abortController?.abort(); // 取消进行中的请求
  }

  // 9. 错误边界:捕获子组件渲染错误
  static getDerivedStateFromError(error) {
    return { hasError: true };
  }

  componentDidCatch(error, info) {
    logErrorToService(error, info.componentStack); // 上报错误监控
  }

  render() {
    if (this.state.hasError) {
      return <ErrorFallback />;
    }
    return <div ref={this.listRef}>{/* JSX */}</div>;
  }
}
2.3.2 函数组件的生命周期替代方案(Hooks)

【现代标准】useEffect 覆盖 90% 的生命周期需求

// ✅ 函数组件等效实现
const ModernComponent = ({ userId }) => {
  const [data, setData] = useState(null);
  const [hasError, setHasError] = useState(false);
  const listRef = useRef(null);
  const abortRef = useRef(null);

  // 等价于 componentDidMount + componentDidUpdate
  useEffect(() => {
    const controller = new AbortController();
    abortRef.current = controller;

    const fetchData = async () => {
      try {
        const response = await fetch(`/api/users/${userId}`, {
          signal: controller.signal,
        });
        const result = await response.json();
        setData(result);
      } catch (err) {
        if (err.name !== 'AbortError') {
          setHasError(true);
        }
      }
    };

    fetchData();

    // 等价于 componentWillUnmount
    return () => {
      controller.abort(); // 清理:取消请求
    };
  }, [userId]); // 依赖数组决定何时执行

  // 等价于 componentDidMount(仅执行一次)
  useEffect(() => {
    const timer = setInterval(updateTime, 1000);
    const handleResize = debounce(handleWindowResize, 200);

    window.addEventListener('resize', handleResize);

    return () => {
      clearInterval(timer);
      window.removeEventListener('resize', handleResize);
    };
  }, []); // 空依赖 = 仅挂载/卸载时执行

  // 错误边界(需要单独的类组件或 react-error-boundary 库)
  if (hasError) {
    throw new Error('Component error'); // 被最近的 ErrorBoundary 捕获
  }

  return <div ref={listRef}>{/* 渲染内容 */}</div>;
};

2.4 Hooks 核心用法与进阶

2.4.1 useState —— 状态管理基础

【核心原则】State 是不可变的(Immutable),每次更新都是替换

🎯 基础用法
// 1. 基本用法
const Counter = () => {
  const [count, setCount] = useState(0); // 初始值为 0

  return (
    <div>
      <p>当前计数:{count}</p>
      <button onClick={() => setCount(count + 1)}>增加</button>
      <button onClick={() => setCount(count - 1)}>减少</button>
      <button onClick={() => setCount(0)}>重置</button>
    </div>
  );
};

// 2. 惰性初始化(初始值为计算密集型时使用)
const ExpensiveInit = ({ initialValue }) => {
  // ❌ 每次渲染都会执行
  // const [data, setData] = useState(computeExpensiveValue(initialValue));

  // ✅ 仅首次渲染执行
  const [data, setData] = useState(() => computeExpensiveValue(initialValue));

  return <div>{data}</div>;
};

// 3. 函数式更新(解决闭包陷阱)
const Incrementer = () => {
  const [count, setCount] = useState(0);

  const incrementMultipleTimes = () => {
    // ❌ 可能只增加 1(闭包问题)
    // setCount(count + 1);
    // setCount(count + 1);
    // setCount(count + 1);

    // ✅ 函数式更新:确保基于最新 state
    setCount(prev => prev + 1);
    setCount(prev => prev + 1);
    setCount(prev => prev + 1);
  };

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={incrementMultipleTimes}>+3</button>
    </div>
  );
};

// 4. 对象/数组的 state 更新(不可变更新模式)
const Form = () => {
  const [form, setForm] = useState({
    username: '',
    email: '',
    preferences: {
      newsletter: false,
      notifications: true,
    },
  });

  // 更新单个字段
  const updateUsername = (value) => {
    setForm(prev => ({
      ...prev,
      username: value, // 展开运算符合并
    }));
  };

  // 更新嵌套字段
  const toggleNewsletter = () => {
    setForm(prev => ({
      ...prev,
      preferences: {
        ...prev.preferences,
        newsletter: !prev.preferences.newsletter,
      },
    }));
  };

  // 数组操作
  const [items, setItems] = useState([]);
  const addItem = (item) => setItems(prev => [...prev, item]);
  const removeItem = (index) => setItems(prev => prev.filter((_, i) => i !== index));
  const updateItem = (index, newItem) =>
    setItems(prev => prev.map((item, i) => i === index ? newItem : item));
};
⚠️ 常见错误
// ❌ 错误 1:直接修改 state(违反不可变原则)
const BadMutation = () => {
  const [list, setList] = useState([1, 2, 3]);

  const addItemWrong = () => {
    list.push(4); // 直接修改!React 无法检测变化
    setList(list); // 不会触发重新渲染
  };

  // ✅ 正确:创建新数组
  const addItemCorrect = () => {
    setList([...list, 4]); // 创建新引用
  };
};

// ❌ 错误 2:在渲染中直接调用 setState(无限循环)
const InfiniteLoop = () => {
  const [count, setCount] = useState(0);

  setCount(count + 1); // 每次渲染都触发更新 → 无限循环!

  return <div>{count}</div>; // 永远不会执行到这里
};

// ✅ 正确:在事件处理或 useEffect 中调用
const CorrectUsage = () => {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const id = setInterval(() => {
      setCount(c => c + 1); // 在副作用中更新
    }, 1000);
    return () => clearInterval(id);
  }, []);

  return <div>{count}</div>;
};
2.4.2 useEffect —— 副作用处理核心

【难点重点】useEffect 是新手最容易出错的地方

🎯 useEffect 执行时机图解
渲染 (render) → 提交到 DOM → 浏览器绘制 → useEffect 执行
                     ↓
              [浏览器可能在此处绘制]
📝 三种典型使用模式
// 模式 1:无依赖(每次渲染后都执行)—— 极少使用
const LogEveryRender = ({ prop }) => {
  useEffect(() => {
    console.log('组件渲染了,prop:', prop); // 每次渲染都执行
  }); // 注意:不要遗漏依赖数组参数

  return <div>{prop}</div>;
};

// 模式 2:空依赖(仅挂载和卸载时执行)—— 最常用
const SubscribeToService = ({ userId }) => {
  useEffect(() => {
    // componentDidMount
    const subscription = userService.subscribe(userId, handleUpdate);

    // componentWillUnmount
    return () => {
      subscription.unsubscribe(); // 清理函数
    };
  }, []); // 空数组 = 仅执行一次

  return <div>User: {userId}</div>;
};

// 模式 3:特定依赖(依赖变化时执行)—— 数据同步场景
const SyncWithProps = ({ userId }) => {
  const [userData, setUserData] = useState(null);

  useEffect(() => {
    let isMounted = true; // 防止卸载后 setState

    const fetchUserData = async () => {
      const data = await api.getUser(userId);
      if (isMounted) { // 组件仍挂载才更新
        setUserData(data);
      }
    };

    fetchUserData();

    return () => {
      isMounted = false; // 清理:标记为已卸载
    };
  }, [userId]); // userId 变化时重新执行

  if (!userData) return <Loading />;

  return <UserProfile {...userData} />;
};
⚠️ 依赖数组规则(必须严格遵守)
// ❌ 错误 1:依赖数组遗漏变量(eslint 会警告)
const MissingDependency = ({ userId }) => {
  const [filter, setFilter] = useState('all');

  useEffect(() => {
    // 使用了 filter 但未加入依赖数组
    fetch(`/api/users/${userId}?filter=${filter}`);
  }, [userId]); // 缺少 filter!

  // ✅ 修复:添加所有外部变量到依赖数组
  useEffect(() => {
    fetch(`/api/users/${userId}?filter=${filter}`);
  }, [userId, filter]); // 完整的依赖
};

// ❌ 错误 2:依赖数组包含过多变量(频繁触发 effect)
const TooManyDependencies = ({ a, b, c, d, e, f }) => {
  useEffect(() => {
    doSomething(a, b, c, d, e, f);
  }, [a, b, c, d, e, f]); // 任意一个变化都会触发

  // ✅ 优化:聚合相关状态或使用 useRef
  const depsRef = useRef({ a, b, c, d, e, f });

  useEffect(() => {
    const { a, b, c, d, e, f } = depsRef.current;
    doSomething(a, b, c, d, e, f);
  }, [/* 手动控制依赖 */]);
};

// ✅ 最佳实践:使用 useMemo/useCallback 优化依赖
const OptimizedDependencies = ({ items, onSelect }) => {
  // 稳定化的回调函数
  const handleSelect = useCallback((id) => {
    onSelect(id);
  }, [onSelect]);

  // 计算属性(避免不必要的 effect 触发)
  const processedItems = useMemo(() => {
    return items.map(item => ({ ...item, processed: true }));
  }, [items]);

  useEffect(() => {
    // 现在 effect 只会在必要时触发
    setupListener(processedItems, handleSelect);
  }, [processedItems, handleSelect]);
};
2.4.3 useRef —— 引用与持久化

【用途】保存不触发渲染的可变值、访问 DOM 元素

🎯 三大典型场景
// 场景 1:访问 DOM 元素(最常见)
const AutoFocusInput = () => {
  const inputRef = useRef(null);

  useEffect(() => {
    // DOM 挂载后自动聚焦
    inputRef.current?.focus();
  }, []);

  return (
    <div>
      <input ref={inputRef} placeholder="自动聚焦的输入框" />
      <button onClick={() => inputRef.current?.focus()}>
        点击聚焦
      </button>
    </div>
  );
};

// 场景 2:保存上次渲染的值(不触发重渲染)
const PreviousValue = ({ value }) => {
  const prevValue = useRef(value);

  useEffect(() => {
    // 更新 ref(不影响渲染)
    prevValue.current = value;
  }, [value]);

  // 显示变化信息
  const changed = prevValue.current !== value;

  return (
    <div style={{ color: changed ? 'red' : 'green' }}>
      当前值:{value} {changed && `(从 ${prevValue.current} 变化)`}
    </div>
  );
};

// 场景 3:存储定时器 ID、WebSocket 实例等(清理必需)
const TimerComponent = () => {
  const [seconds, setSeconds] = useState(0);
  const intervalRef = useRef(null);

  const startTimer = () => {
    if (intervalRef.current) return; // 防止重复启动

    intervalRef.current = setInterval(() => {
      setSeconds(s => s + 1);
    }, 1000);
  };

  const stopTimer = () => {
    if (intervalRef.current) {
      clearInterval(intervalRef.current);
      intervalRef.current = null;
    }
  };

  // 组件卸载时自动清理
  useEffect(() => {
    return () => {
      if (intervalRef.current) {
        clearInterval(intervalRef.current);
      }
    };
  }, []);

  return (
    <div>
      <p>计时:{seconds}秒</p>
      <button onClick={startTimer}>开始</button>
      <button onClick={stopTimer}>停止</button>
    </div>
  );
};
⚠️ useRef vs useState 选择指南
场景 使用 useRef 使用 useState
需要触发 UI 更新
访问 DOM 元素
保存定时器/订阅引用
存储上一轮的值
存储计算结果(需要渲染)
2.4.4 useContext —— 跨组件状态共享

【适用场景】主题、语言、用户信息等全局状态

🎯 基础用法
// 1. 创建 Context
const ThemeContext = createContext({
  theme: 'light',
  toggleTheme: () => {},
});

// 2. 提供者组件(通常在应用顶层)
const App = () => {
  const [theme, setTheme] = useState('light');

  const toggleTheme = () => {
    setTheme(prev => (prev === 'light' ? 'dark' : 'light'));
  };

  const contextValue = useMemo(() => ({
    theme,
    toggleTheme,
  }), [theme]);

  return (
    <ThemeContext.Provider value={contextValue}>
      <Header />
      <MainContent />
      <Footer />
    </ThemeContext.Provider>
  );
};

// 3. 消费者组件(任意层级)
const ThemedButton = () => {
  const { theme, toggleTheme } = useContext(ThemeContext);

  return (
    <button
      onClick={toggleTheme}
      className={`btn btn--${theme}`}
    >
      切换为{theme === 'light' ? '深色' : '浅色'}模式
    </button>
  );
};

// 4. 也可以在 Class 组件中使用(render props 模式)
class LegacyButton extends React.Component {
  render() {
    return (
      <ThemeContext.Consumer>
        {({ theme }) => (
          <button className={`btn btn--${theme}`}>
            类组件按钮
          </button>
        )}
      </ThemeContext.Consumer>
    );
  }
}
⚠️ 性能陷阱与优化
// ❌ 性能问题:Context 值变化导致所有消费者重渲染
const BadProvider = ({ children }) => {
  const [user, setUser] = useState(null);
  const [theme, setTheme] = useState('light');

  // 每次渲染都创建新对象 → 所有消费者都会重渲染
  return (
    <AppContext.Provider value={{
      user,
      theme,
      setUser,
      setTheme, // 函数引用每次都变!
    }}>
      {children}
    </AppContext.Provider>
  );
};

// ✅ 优化方案 1:拆分多个 Context(最小化重渲染范围)
const UserContext = createContext(null);
const ThemeContext = createContext(null);

const OptimizedProvider = ({ children }) => {
  const [user, setUser] = useState(null);
  const [theme, setTheme] = useState('light');

  return (
    <UserContext.Provider value={user}>
      <ThemeContext.Provider value={{ theme, setTheme }}>
        {children}
      </ThemeContext.Provider>
    </UserContext.Provider>
  );
};

// ✅ 优化方案 2:使用 useMemo 稳定化值
const StableProvider = ({ children }) => {
  const [user, setUser] = useState(null);
  const [theme, setTheme] = useState('light');

  const memoizedValue = useMemo(() => ({
    user,
    setUser, // setState 本身是稳定的
    theme,
    setTheme: useCallback(() => { // 包装成稳定引用
      setTheme(t => t === 'light' ? 'dark' : 'light');
    }, []),
  }), [user, theme]);

  return (
    <AppContext.Provider value={memoizedValue}>
      {children}
    </AppContext.Provider>
  );
};

2.5 组件通信架构设计

2.5.1 父子组件通信(Props Down, Events Up)

【单向数据流】React 的核心原则

// 父组件:状态提升(Lifting State Up)
const TodoApp = () => {
  const [todos, setTodos] = useState([
    { id: 1, text: '学习 React', completed: false },
    { id: 2, text: '完成作业', completed: true },
  ]);

  // 定义操作函数(向下传递给子组件)
  const addTodo = (text) => {
    const newTodo = {
      id: Date.now(), // 生产环境应使用 UUID
      text,
      completed: false,
    };
    setTodos(prev => [...prev, newTodo]);
  };

  const toggleTodo = (id) => {
    setTodos(prev =>
      prev.map(todo =>
        todo.id === id ? { ...todo, completed: !todo.completed } : todo
      )
    );
  };

  const deleteTodo = (id) => {
    setTodos(prev => prev.filter(todo => todo.id !== id));
  };

  return (
    <div className="todo-app">
      <h1>待办事项</h1>
      {/* 向下传递数据和操作函数 */}
      <TodoForm onAdd={addTodo} />
      <TodoList
        todos={todos}
        onToggle={toggleTodo}
        onDelete={deleteTodo}
      />
      <TodoStats todos={todos} />
    </div>
  );
};

// 子组件 1:表单(接收回调函数)
const TodoForm = ({ onAdd }) => {
  const [text, setText] = useState('');

  const handleSubmit = (e) => {
    e.preventDefault();
    if (!text.trim()) return;

    onAdd(text.trim()); // 调用父组件传递的函数
    setText('');
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={text}
        onChange={(e) => setText(e.target.value)}
        placeholder="添加新的待办事项..."
      />
      <button type="submit">添加</button>
    </form>
  );
};

// 子组件 2:列表(展示数据 + 触发事件)
const TodoList = ({ todos, onToggle, onDelete }) => {
  if (todos.length === 0) {
    return <EmptyState message="暂无待办事项" />;
  }

  return (
    <ul className="todo-list">
      {todos.map(todo => (
        <TodoItem
          key={todo.id}
          todo={todo}
          onToggle={() => onToggle(todo.id)}
          onDelete={() => onDelete(todo.id)}
        />
      ))}
    </ul>
  );
};
2.5.2 兄弟组件通信(状态提升 + Context)

【架构原则】共享状态应该提升到最近的共同祖先组件

方案 1:状态提升(适用于简单场景)
// 共同父组件:管理共享状态
const ProductPage = () => {
  const [selectedCategoryId, setSelectedCategoryId] = useState(null);

  return (
    <div className="product-page">
      <aside>
        {/* 兄弟组件 1:选择分类 */}
        <CategoryList
          selectedId={selectedCategoryId}
          onSelect={setSelectedCategoryId}
        />
      </aside>
      <main>
        {/* 兄弟组件 2:根据分类显示产品 */}
        <ProductGrid categoryId={selectedCategoryId} />
      </main>
    </div>
  );
};
方案 2:Context(适用于深层嵌套或多层兄弟通信)
// 创建共享 Context
const SelectedCategoryContext = createContext(null);

// 顶层提供数据
const App = () => {
  const [selectedCategory, setSelectedCategory] = useState(null);

  return (
    <SelectedCategoryContext.Provider value={{
      selectedCategory,
      setSelectedCategory,
    }}>
      <ProductPage />
    </SelectedCategoryContext.Provider>
  );
};

// 任意层级的兄弟组件都可以消费
const CategoryList = () => {
  const { selectedCategory, setSelectedCategory } = useContext(SelectedCategoryContext);
  // ... 使用共享状态
};

const ProductGrid = () => {
  const { selectedCategory } = useContext(SelectedCategoryContext);
  // ... 使用共享状态
};
2.5.3 跨层级通信(Context / Event Bus / 状态管理库)

【选型依据】根据项目规模和复杂度选择合适方案

方案 适用场景 复杂度 性能 可扩展性
Props Drilling 1-2 层传递 ⭐⭐⭐⭐⭐ ⭐⭐
Context API 全局状态(主题/语言/用户) ⭐⭐⭐⭐ ⭐⭐⭐⭐
useReducer + Context 复杂全局状态 中高 ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
Redux/Zustand 大型应用、多人协作 ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
Event Bus 松耦合组件通知 ⭐⭐⭐⭐ ⭐⭐
🎯 实战:构建可扩展的状态管理层
// store/auth-context.js
const AuthContext = createContext(null);

const initialState = {
  user: null,
  token: null,
  isAuthenticated: false,
  isLoading: false,
  error: null,
};

function authReducer(state, action) {
  switch (action.type) {
    case 'LOGIN_START':
      return { ...state, isLoading: true, error: null };
    case 'LOGIN_SUCCESS':
      return {
        ...state,
        isLoading: false,
        isAuthenticated: true,
        user: action.payload.user,
        token: action.payload.token,
      };
    case 'LOGIN_FAILURE':
      return {
        ...state,
        isLoading: false,
        error: action.payload.error,
        isAuthenticated: false,
      };
    case 'LOGOUT':
      return { ...initialState };
    default:
      return state;
  }
}

export function AuthProvider({ children }) {
  const [state, dispatch] = useReducer(authReducer, initialState);

  // 使用 useMemo 避免不必要的重渲染
  const contextValue = useMemo(() => ({
    ...state,
    login: async (credentials) => {
      dispatch({ type: 'LOGIN_START' });
      try {
        const result = await authService.login(credentials);
        dispatch({ type: 'LOGIN_SUCCESS', payload: result });
      } catch (error) {
        dispatch({ type: 'LOGIN_FAILURE', payload: { error: error.message } });
      }
    },
    logout: () => {
      dispatch({ type: 'LOGOUT' });
      authService.clearToken();
    },
  }), [state]);

  return (
    <AuthContext.Provider value={contextValue}>
      {children}
    </AuthContext.Provider>
  );
}

export const useAuth = () => {
  const context = useContext(AuthContext);
  if (!context) {
    throw new Error('useAuth must be used within AuthProvider');
  }
  return context;
};

三、基础生态工具

3.1 路由管理:React Router v6

3.1.1 核心概念与配置

【架构要点】路由即组件,声明式配置优于命令式导航

// router/index.jsx
import { createBrowserRouter, RouterProvider, Navigate } from 'react-router-dom';

// 路由配置(集中管理,便于维护)
const router = createBrowserRouter([
  {
    path: '/',
    element: <Layout />,
    errorElement: <ErrorBoundary />, // 全局错误处理
    children: [
      {
        index: true, // 默认子路由(等同于 path: '')
        element: <HomePage />,
      },
      {
        path: 'about',
        element: <AboutPage />,
      },
      {
        path: 'products',
        element: <ProductsLayout />, // 嵌套路由的布局组件
        children: [
          {
            index: true,
            element: <ProductList />,
          },
          {
            path: ':productId', // 动态参数
            element: <ProductDetail />,
            loader: productLoader, // 数据预加载
          },
        ],
      },
      {
        path: 'dashboard',
        element: (
          <ProtectedRoute> {/* 路由守卫 */}
            <DashboardPage />
          </ProtectedRoute>
        ),
        children: [
          { path: 'profile', element: <ProfilePage /> },
          { path: 'settings', element: <SettingsPage /> },
        ],
      },
      {
        path: '*',
        element: <NotFoundPage />, // 404 页面
      },
    ],
  },
]);

// App 入口
const App = () => {
  return <RouterProvider router={router} />;
};
3.1.2 路由导航与传参

【三种传参方式】动态参数 / searchParams / state

// 1. 动态参数(URL 路径的一部分)
const ProductDetail = () => {
  const { productId } = useParams(); // 获取 :productId
  const location = useLocation();

  const product = useLoaderData(); // 使用 loader 预加载的数据

  return (
    <div>
      <h1>产品详情 #{productId}</h1>
      <p>完整路径:{location.pathname}</p>
      <ProductInfo product={product} />
    </div>
  );
};

// 2. Search Params(查询参数)
const SearchResults = () => {
  const [searchParams, setSearchParams] = useSearchParams();
  const query = searchParams.get('q');
  const page = parseInt(searchParams.get('page') || '1');

  const handleSearch = (newQuery) => {
    setSearchParams({ q: newQuery, page: '1' }); // 更新查询参数
  };

  return (
    <div>
      <SearchBar
        defaultValue={query}
        onSearch={handleSearch}
      />
      <ResultList query={query} page={page} />
      <Pagination
        currentPage={page}
        onPageChange={(newPage) =>
          setSearchParams({ q: query, page: String(newPage) })
        }
      />
    </div>
  );
};

// 3. 导航状态(不显示在 URL 中,页面刷新后丢失)
const UserList = () => {
  const navigate = useNavigate();

  const handleUserClick = (user) => {
    navigate(`/users/${user.id}`, {
      state: { from: 'list', timestamp: Date.now() }, // 传递隐藏状态
    });
  };

  return (
    <table>
      {users.map(user => (
        <tr key={user.id} onClick={() => handleUserClick(user)}>
          <td>{user.name}</td>
        </tr>
      ))}
    </table>
  );
};

// 接收端读取 state
const UserDetail = () => {
  const { id } = useParams();
  const location = useLocation();
  const navigationState = location.state; // { from: 'list', timestamp: ... }

  useEffect(() => {
    if (navigationState?.from === 'list') {
      analytics.track('view_from_list', { userId: id });
    }
  }, [id, navigationState]);
};
3.1.3 路由守卫与权限控制

【安全关键】前端路由守卫只是 UX 优化,真正的权限验证必须在后端

// components/ProtectedRoute.jsx
const ProtectedRoute = ({ children, requiredRoles = [] }) => {
  const { isAuthenticated, user, isLoading } = useAuth();
  const location = useLocation();

  // 加载中状态
  if (isLoading) {
    return <FullPageLoading />;
  }

  // 未登录:重定向到登录页,记录来源地址
  if (!isAuthenticated) {
    return <Navigate to="/login" state={{ from: location }} replace />;
  }

  // 角色权限检查
  if (requiredRoles.length > 0 && !requiredRoles.includes(user.role)) {
    return <Navigate to="/unauthorized" replace />;
  }

  return children;
};

// 使用示例
const router = createBrowserRouter([
  {
    path: '/admin',
    element: (
      <ProtectedRoute requiredRoles={['admin', 'super_admin']}>
        <AdminPanel />
      </ProtectedRoute>
    ),
  },
  {
    path: '/profile',
    element: (
      <ProtectedRoute>
        <ProfilePage />
      </ProtectedRoute>
    ),
  },
]);

3.2 状态管理方案选型

3.2.1 原生 Context + useReducer(中小型项目首选)

【优势】零依赖、React 内置、足够应对大多数场景

🎯 完整示例:购物车状态管理
// store/cart-context.js
const CartContext = createContext(null);

const CART_ACTIONS = {
  ADD_ITEM: 'ADD_ITEM',
  REMOVE_ITEM: 'REMOVE_ITEM',
  UPDATE_QUANTITY: 'UPDATE_QUANTITY',
  CLEAR_CART: 'CLEAR_CART',
  APPLY_COUPON: 'APPLY_COUPON',
};

const initialState = {
  items: [], // [{ id, name, price, quantity, image }]
  couponCode: null,
  discount: 0,
  subtotal: 0,
  total: 0,
};

function cartReducer(state, action) {
  switch (action.type) {
    case CART_ACTIONS.ADD_ITEM: {
      const existingIndex = state.items.findIndex(
        item => item.id === action.payload.id
      );

      if (existingIndex >= 0) {
        // 商品已存在,增加数量
        const updatedItems = [...state.items];
        updatedItems[existingIndex] = {
          ...updatedItems[existingIndex],
          quantity: updatedItems[existingIndex].quantity + 1,
        };
        return calculateTotals({ ...state, items: updatedItems });
      }

      // 新商品
      const newItems = [...state.items, { ...action.payload, quantity: 1 }];
      return calculateTotals({ ...state, items: newItems });
    }

    case CART_ACTIONS.REMOVE_ITEM: {
      const filteredItems = state.items.filter(
        item => item.id !== action.payload
      );
      return calculateTotals({ ...state, items: filteredItems });
    }

    case CART_ACTIONS.UPDATE_QUANTITY: {
      const { itemId, quantity } = action.payload;
      if (quantity <= 0) {
        return cartReducer(state, { type: CART_ACTIONS.REMOVE_ITEM, payload: itemId });
      }

      const updatedItems = state.items.map(item =>
        item.id === itemId ? { ...item, quantity } : item
      );
      return calculateTotals({ ...state, items: updatedItems });
    }

    case CART_ACTIONS.CLEAR_CART:
      return initialState;

    case CART_ACTIONS.APPLY_COUPON: {
      const discount = calculateDiscount(state.subtotal, action.payload);
      return { ...state, couponCode: action.payload, discount };
    }

    default:
      return state;
  }
}

// 辅助函数:计算总价
function calculateTotals(state) {
  const subtotal = state.items.reduce(
    (sum, item) => sum + item.price * item.quantity,
    0
  );
  const total = subtotal - state.discount;
  return { ...state, subtotal, total };
}

export function CartProvider({ children }) {
  const [cart, dispatch] = useReducer(cartReducer, initialState);

  const contextValue = useMemo(() => ({
    ...cart,
    addItem: (product) => dispatch({ type: CART_ACTIONS.ADD_ITEM, payload: product }),
    removeItem: (itemId) => dispatch({ type: CART_ACTIONS.REMOVE_ITEM, payload: itemId }),
    updateQuantity: (itemId, quantity) =>
      dispatch({ type: CART_ACTIONS.UPDATE_QUANTITY, payload: { itemId, quantity } }),
    clearCart: () => dispatch({ type: CART_ACTIONS.CLEAR_CART }),
    applyCoupon: (code) => dispatch({ type: CART_ACTIONS.APPLY_COUPON, payload: code }),
  }), [cart]);

  return (
    <CartContext.Provider value={contextValue}>
      {children}
    </CartContext.Provider>
  );
}

export const useCart = () => {
  const context = useContext(CartContext);
  if (!context) {
    throw new Error('useCart must be used within CartProvider');
  }
  return context;
};
3.2.2 Redux Toolkit(大型项目标准)

【何时升级到 Redux】 当出现以下信号时考虑引入:

  • 多个不相关的组件需要访问相同状态
  • 状态更新逻辑复杂,需要中间件(日志、持久化、异步)
  • 需要时间旅行调试(DevTools)
  • 团队规模较大,需要统一的状态管理模式
// store/index.js (Redux Toolkit)
import { configureStore, createSlice } from '@reduxjs/toolkit';

// 1. 创建 Slice( reducer + actions)
const counterSlice = createSlice({
  name: 'counter',
  initialState: {
    value: 0,
    status: 'idle',
  },
  reducers: {
    increment: (state) => {
      state.value += 1; // Immer 允许直接修改(内部转换为不可变更新)
    },
    decrement: (state) => {
      state.value -= 1;
    },
    incrementByAmount: (state, action) => {
      state.value += action.payload;
    },
  },
  // 异步 thunk
  extraReducers: (builder) => {
    builder
      .addCase(incrementAsync.pending, (state) => {
        state.status = 'loading';
      })
      .addCase(incrementAsync.fulfilled, (state, action) => {
        state.status = 'idle';
        state.value += action.payload;
      });
  },
});

// 2. 异步 Action Creator
export const incrementAsync = (amount) => async (dispatch) => {
  dispatch(counterSlice.actions.increment());
  // 模拟 API 调用
  const result = await api.fetchIncrement(amount);
  dispatch(incrementByAmount(result.value));
};

// 3. 配置 Store
export const store = configureStore({
  reducer: {
    counter: counterSlice.reducer,
    // 其他 slice...
  },
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({
      serializableCheck: false, // 根据需要配置
    }),
});

// App.js
import { Provider } from 'react-redux';
import { store } from './store';

const App = () => (
  <Provider store={store}>
    <RouterProvider router={router} />
  </Provider>
);

// 组件中使用
const CounterComponent = () => {
  const count = useSelector((state) => state.counter.value);
  const dispatch = useDispatch();

  return (
    <div>
      <span>{count}</span>
      <button onClick={() => dispatch(increment())}>+</button>
      <button onClick={() => dispatch(decrement())}>-</button>
    </div>
  );
};

3.3 工程化配置与构建优化

3.3.1 项目初始化:Vite vs CRA(Create React App)

【2026年推荐】Vite 是事实标准,CRA 已进入维护模式

维度 Vite (推荐) CRA (Legacy)
启动速度 ⚡ 毫秒级(原生 ESM) 🐢 10-30秒(Webpack 打包)
热更新速度 ⚡ 即时更新 🐢 2-10秒
配置灵活性 ✅ 高度可定制 ⚠️ eject 后才能定制
生态成熟度 ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐
TypeScript 支持 ✅ 原生支持 ✅ 支持
官方维护状态 🟢 活跃开发 🟡 维护模式(不再添加新特性)
🚀 Vite 项目初始化与配置
# 创建项目
npm create vite@latest my-react-app -- --template react-ts
cd my-react-app
npm install

# 安装常用依赖
npm install react-router-dom axios
npm install -D @types/node tailwindcss postcss autoprefixer
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';

export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src'), // 路径别名
    },
  },
  server: {
    port: 3000,
    open: true,
    proxy: {
      '/api': {
        target: 'http://localhost:8080', // 后端服务地址
        changeOrigin: true,
        rewrite: (path) => path.replace(/^\/api/, ''),
      },
    },
  },
  build: {
    outDir: 'dist',
    sourcemap: false, // 生产环境关闭 sourcemap(安全考虑)
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['react', 'react-dom'], // 第三方库分包
          router: ['react-router-dom'],
        },
      },
    },
  },
});
3.3.2 包管理器选择

【团队一致性】选择一种并在整个项目中统一使用

包管理器 锁文件 优点 缺点 适用场景
npm package-lock.json Node.js 官方、兼容性好 速度较慢 传统项目、CI/CD
yarn yarn.lock 速度快、离线模式 有时版本冲突 大型 monorepo
pnpm pnpm-lock.yaml 节省磁盘空间、严格依赖 生态相对较新 现代项目(强烈推荐)
📦 pnpm 配置示例
# 安装 pnpm
npm install -g pnpm

# 项目初始化
pnpm create vite my-app --template react
cd my-app
pnpm install

# 常用命令
pnpm add axios           # 安装依赖
pnpm add -D @types/react  # 安装开发依赖
pnpm remove lodash        # 移除依赖
pnpm update              # 更新依赖
pnpm audit               # 安全审计
// .npmrc(项目级别配置)
shamefully-hoist=false
strict-peer-dependencies=false
save-exact=true          # 锁定精确版本
3.3.3 ESM 模块化(ECMAScript Modules)

【现代标准】ESM 是 JavaScript 的官方模块系统,取代 CommonJS

// 导出(Named Exports)
// utils/helpers.js
export const formatDate = (date) => { /* ... */ };
export const formatCurrency = (amount) => { /* ... */ };
export const debounce = (fn, delay) => { /* ... */ };

// 默认导出(Default Export)
// components/Button.jsx
export default function Button({ children, onClick }) {
  return <button onClick={onClick}>{children}</button>;
}

// 导入使用
import Button, { formatDate, formatCurrency } from '@/components/Button';
import { debounce } from '@/utils/helpers';

// 动态导入(Code Splitting)
const HeavyComponent = lazy(() => import('./HeavyComponent'));

// 动态导入(按需加载)
const loadModule = async () => {
  const module = await import(`./modules/${moduleName}`);
  module.init();
};

3.4 UI 框架集成规范

3.4.1 Ant Design(企业级后台首选)

【集成要点】按需引入、主题定制、国际化

// 1. 安装
npm install antd @ant-design/icons

// 2. 按需引入(Vite + unplugin-auto-import)
// vite.config.ts
import AutoImport from 'unplugin-auto-import/vite';

export default defineConfig({
  plugins: [
    AutoImport({
      resolvers: [
        (name) => {
          if (name.startsWith('A')) {
            const component = name.slice(1);
            return { importName: component, from: 'antd' };
          }
        },
      ],
    }),
  ],
});

// 3. 主题定制(Design Token)
// App.jsx 或入口文件
import { ConfigProvider, theme } from 'antd';

const App = () => (
  <ConfigProvider
    theme={{
      algorithm: theme.darkAlgorithm, // 暗色主题
      token: {
        colorPrimary: '#1890ff', // 主色调
        borderRadius: 8,
      },
      components: {
        Button: {
          primaryShadow: 'none', // 移除按钮阴影
        },
      },
    }}
    locale={zhCN} // 国际化
  >
    <RouterProvider router={router} />
  </ConfigProvider>
);

// 4. 业务组件封装(统一规范)
const ProTable = ({
  columns,
  dataSource,
  loading,
  pagination,
  onPaginationChange,
  ...restProps
}) => {
  return (
    <Table
      columns={columns}
      dataSource={dataSource}
      loading={loading}
      pagination={{
        current: pagination.page,
        pageSize: pagination.pageSize,
        total: pagination.total,
        showSizeChanger: true,
        showQuickJumper: true,
        showTotal: (total) => `共 ${total} 条`,
        onChange: (page, pageSize) => {
          onPaginationChange?.({ page, pageSize });
        },
      }}
      rowKey="id"
      size="middle"
      {...restProps}
    />
  );
};
3.4.2 UI 框架选型对比
框架 适用场景 定制难度 Bundle Size 社区活跃度
Ant Design 企业后台、B端产品 ⭐⭐ 较大 ⭐⭐⭐⭐⭐
Material UI 产品导向、Material Design ⭐⭐⭐ 较大 ⭐⭐⭐⭐⭐
Arco Design 字节系、现代化界面 ⭐⭐ 中等 ⭐⭐⭐⭐
Headless UI 高度定制需求 ⭐⭐⭐⭐⭐ 极小 ⭐⭐⭐
Radix UI 无障碍优先 ⭐⭐⭐⭐ ⭐⭐⭐⭐
🎯 UI 组件二次封装原则
// 1. 统一接口规范
interface TableProps<T> {
  columns: ColumnType<T>[];
  dataSource: T[];
  loading?: boolean;
  pagination?: PaginationConfig;
  rowSelection?: TableRowSelection<T>;
  onRowClick?: (record: T) => void;
  emptyText?: React.ReactNode;
}

// 2. 统一错误处理
const AsyncButton = ({ onClick, ...props }) => {
  const [loading, setLoading] = useState(false);

  const handleClick = async (e) => {
    setLoading(true);
    try {
      await onClick(e);
    } finally {
      setLoading(false);
    }
  };

  return <Button loading={loading} onClick={handleClick} {...props} />;
};

// 3. 统一数据格式转换
const transformApiDataToTableColumns = (fields) => {
  return fields.map(field => ({
    title: field.label,
    dataIndex: field.key,
    key: field.key,
    width: field.width,
    fixed: field.fixed,
    render: field.render || ((value) => value ?? '-'), // 空值显示
  }));
};

四、调试与代码规范

4.1 React DevTools 高级调试

4.1.1 组件面板深度使用

【核心功能】不仅仅是查看 Props 和 State

🔍 主要功能区域
┌─────────────────────────────────────────────┐
│  Components 面板                             │
├─────────────────────────────────────────────┤
│  ┌─ <App>                                   │
│  │  ├─ <Router>                             │
│  │  │  ├─ <Routes>                          │
│  │  │  │  ├─ <Layout>                       │
│  │  │  │  │  ├─ <Header> ✓ 已挂载           │
│  │  │  │  │  ├─ <Sidebar>                   │
│  │  │  │  │  └─ <MainContent>               │
│  │  │  │  │     ├─ <UserList>               │
│  │  │  │  │     │  ├─ <UserCard> × 10      │
│  │  │  │  │     │  └─ <UserCard> ⚡ 选中    │
│  │  │  │  │     └─ <Pagination>             │
└─────────────────────────────────────────────┘
🎯 高级调试技巧
// 1. 在控制台访问选中组件实例
// 选中组件后,在 Console 输入:
$r.props      // 查看 Props
$r.state      // 查看 State(类组件)
$r.hooks      // 查看 Hooks 状态
$r.forceUpdate()  // 强制重新渲染(调试用)

// 2. 追踪组件渲染原因(React 18+)
// 在组件中添加:
const whyDidYouRender = require('@welldone-software/why-did-you-render');

if (process.env.NODE_ENV === 'development') {
  whyDidYouRender(React, {
    trackAllPureComponents: true,
  });
}

// 3. 使用 React Developer Tools Profiler 录制性能
// - 点击录制按钮
// - 执行操作(如点击、输入)
// - 停止录制,查看 Flame Chart
// - 识别渲染耗时的组件(红色标记)
4.1.2 Profiler 性能分析

【优化入口】识别性能瓶颈的第一步

Flame Chart(火焰图)示例:

<App> ████████████████████████ 120ms
  <Router> ██████████████████ 100ms
    <Layout> ██████████████ 80ms
      <UserList> ██████████ 60ms  ← 瓶颈组件
        <UserCard> ████ 15ms × 20  ← 重复渲染
        <UserCard> ████ 15ms × 20
      <Sidebar> ██ 10ms
      <Header> ██ 8ms
🎯 优化前后对比
// ❌ 优化前:每个 UserCard 都独立重渲染
const UserListBad = ({ users, onSelect }) => (
  <div>
    {users.map(user => (
      <UserCard
        key={user.id}
        user={user}
        onSelect={onSelect} // 每次都是新函数引用!
      />
    ))}
  </div>
);

// ✅ 优化后:使用 React.memo + useCallback
const UserCardMemo = React.memo(function UserCard({ user, onSelect }) {
  console.log(`Rendering UserCard: ${user.id}`); // 观察渲染次数
  return (
    <div onClick={() => onSelect(user.id)}>
      {user.name}
    </div>
  );
});

const UserListGood = ({ users, onSelect }) => {
  const handleSelect = useCallback((id) => {
    onSelect(id);
  }, [onSelect]); // 稳定化回调

  return (
    <div>
      {users.map(user => (
        <UserCardMemo
          key={user.id}
          user={user}
          onSelect={handleSelect}
        />
      ))}
    </div>
  );
};

4.2 ESLint 与代码质量保障

4.2.1 ESLint 配置(React 项目标准配置)

【规则制定】严格的 Lint 规则是工程质量的第一道防线

// .eslintrc.cjs
module.exports = {
  env: {
    browser: true,
    es2021: true,
    node: true,
  },
  extends: [
    'eslint:recommended',
    'plugin:react/recommended',
    'plugin:react-hooks/recommended', // Hooks 规则(必须!)
    'plugin:@typescript-eslint/recommended',
    'prettier', // 与 Prettiet 集成
  ],
  parser: '@typescript-eslint/parser',
  parserOptions: {
    ecmaVersion: 'latest',
    sourceType: 'module',
    ecmaFeatures: {
      jsx: true,
    },
  },
  plugins: ['react', 'react-hooks', '@typescript-eslint'],
  rules: {
    // React 相关规则
    'react/react-in-jsx-scope': 'off', // React 17+ 不需要导入 React
    'react/prop-types': 'off', // TypeScript 替代 PropTypes
    'react/no-unescaped-entities': 'off',

    // Hooks 规则(防止常见错误)
    'react-hooks/rules-of-hooks': 'error', // Hooks 只能在组件顶层调用
    'react-hooks/exhaustive-deps': 'warn', // useEffect 依赖完整性检查

    // TypeScript 规则
    '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
    '@typescript-eslint/no-explicit-any': 'warn', // 避免使用 any

    // 代码质量规则
    'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
    'no-debugger': 'error',
    eqeqeq: ['error', 'always'], // 强制使用 ===
    'prefer-const': 'error',
    'no-var': 'error',
  },
  settings: {
    react: {
      version: 'detect', // 自动检测 React 版本
    },
  },
};
4.2.2 Prettier 格式化配置

【风格统一】让团队成员不再争论代码格式

// .prettierrc
{
  "semi": true,
  "singleQuote": true,
  "tabWidth": 2,
  "useTabs": false,
  "trailingComma": "es5",
  "printWidth": 100,
  "bracketSpacing": true,
  "arrowParens": "always",
  "endOfLine": "lf",
  "jsxSingleQuote": false,
  "jsxBracketSameLine": false
}
// package.json scripts
{
  "scripts": {
    "lint": "eslint 'src/**/*.{js,jsx,ts,tsx}'",
    "lint:fix": "eslint 'src/**/*.{js,jsx,ts,tsx}' --fix",
    "format": "prettier --write 'src/**/*.{js,jsx,ts,tsx,css,json}'",
    "prepare": "husky install"
  }
}
4.2.3 Git Hooks(提交前自动检查)

【自动化】使用 Husky + lint-staged 保证代码质量

# 安装依赖
npm install -D husky lint-staged

# 初始化 Husky
npx husky install
npx husky add .husky/pre-commit "npx lint-staged"
npx husky add .husky/commit-msg "npx commitlint --edit $1"
// package.json
{
  "lint-staged": {
    "*.{js,jsx,ts,tsx}": [
      "eslint --fix",
      "prettier --write"
    ],
    "*.{css,scss,less}": [
      "stylelint --fix",
      "prettier --write"
    ],
    "*.{json,md}": [
      "prettier --write"
    ]
  }
}

4.3 工程化规范体系

4.3.1 目录结构规范(分层架构)

【可维护性】清晰的目录结构是大型项目的基础

src/
├── assets/                 # 静态资源(图片、字体、图标)
│   ├── images/
│   ├── fonts/
│   └── icons/
├── components/             # 通用组件(可复用)
│   ├── ui/                 # 基础 UI 组件(Button, Input, Modal...)
│   │   ├── Button/
│   │   │   ├── Button.jsx
│   │   │   ├── Button.module.css
│   │   │   └── index.ts
│   │   └── Input/
│   └── business/           # 业务组件(与具体业务相关)
│       ├── UserCard/
│       └── ProductTable/
├── hooks/                  # 自定义 Hooks
│   ├── useAuth.js
│   ├── useDebounce.js
│   └── useRequest.js
├── pages/                  # 页面组件(路由级别)
│   ├── Home/
│   │   ├── index.jsx
│   │   ├── Home.module.css
│   │   └── components/     # 页面专属组件
│   ├── About/
│   └── Dashboard/
├── services/               # API 服务层
│   ├── api.js              # Axios 实例
│   ├── auth.service.js     # 认证相关 API
│   └── user.service.js     # 用户相关 API
├── store/                  # 状态管理
│   ├── context/            # Context 实现
│   └── slices/             # Redux Slices
├── utils/                  # 工具函数
│   ├── helpers.js
│   ├── constants.js
│   └── validators.js
├── styles/                 # 全局样式
│   ├── variables.css       # CSS 变量
│   ├── global.css          # 全局样式
│   └── mixins.css          # 样式混入
├── types/                  # TypeScript 类型定义
│   ├── index.d.ts
│   └── api.types.ts
├── router/                 # 路由配置
│   └── index.tsx
├── App.jsx                 # 应用根组件
├── main.jsx                # 应用入口
└── vite-env.d.ts           # Vite 类型声明
4.3.2 命名规范(团队协作基础)
类型 命名规范 示例
组件文件 PascalCase UserProfile.jsxDataTable.jsx
工具函数 camelCase formatDate.jscalculateTotal.js
常量 UPPER_SNAKE_CASE API_BASE_URLMAX_RETRY_COUNT
CSS 类名 kebab-case (BEM) .user-card__title--active
TypeScript 接口 PascalCase interface IUserProps
枚举 PascalCase enum UserRole
事件处理 handle + 动作 handleSubmithandleClick
布尔值 is/has/can/should isLoadingisVisiblehasPermission
4.3.3 Git Commit 规范(Conventional Commits)

【可追溯性】规范的提交信息便于生成 Changelog 和回滚

<type>(<scope>): <subject>

<body>

<footer>
📝 Type 类型说明
Type 描述 示例
feat 新功能 feat(auth): add OAuth2 login support
fix Bug 修复 fix(cart): resolve quantity calculation error
docs 文档变更 docs(readme): update installation guide
style 代码格式(不影响运行) style(button): fix indentation
refactor 重构(非新功能、非修复) refactor(user): extract validation logic
perf 性能优化 perf(list): implement virtual scrolling
test 测试相关 test(login): add unit tests for form validation
chore 构建/工具变动 chore(deps): upgrade react to 18.2.0
🔧 配置 Commitlint
// commitlint.config.js
module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'type-enum': [
      2,
      'always',
      ['feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'chore', 'revert'],
    ],
    'subject-max-length': [2, 'always', 72], // 主题不超过 72 字符
  },
};
4.3.4 代码审查清单(Code Review Checklist)

【质量保障】PR 提交前的自检清单

✅ 功能性检查
  •  代码实现了预期的功能吗?
  •  是否处理了边界情况(空值、异常输入)?
  •  是否有必要的单元测试?
  •  API 调用是否有错误处理?
✅ 代码质量检查
  •  命名是否清晰表达意图?
  •  函数/组件是否遵循单一职责原则?
  •  是否消除了重复代码(DRY 原则)?
  •  注释是否必要且准确?(好的代码不需要注释解释"做什么",而是"为什么")
✅ 性能检查
  •  是否有不必要的重渲染?(使用 React.memo、useMemo、useCallback)
  •  大列表是否使用了虚拟滚动?
  •  图片/资源是否懒加载?
  •  Bundle size 是否过大?(分析 webpack/vite bundle report)
✅ 安全性检查
  •  是否有 XSS 风险?(避免 dangerouslySetInnerHTML,或使用 DOMPurify)
  •  敏感数据是否明文存储?(Token、密码等应加密)
  •  API 请求是否有 CSRF 防护?
  •  用户输入是否经过校验和清洗?
✅ 可访问性(Accessibility)
  •  图片是否有 alt 属性?
  •  表单元素是否有关联的 label?
  •  颜色是否不是唯一的区分方式?(考虑色盲用户)
  •  键盘导航是否正常工作?

附录:学习路线图与资源推荐

📚 官方资源(必读)

  1. React 官方文档https://react.dev (最新版,推荐从头到尾学习)
  2. React Router 文档https://reactrouter.com (v6 最新特性)
  3. Redux Toolkit 文档https://redux-toolkit.js.org (现代 Redux 用法)

🛠️ 推荐工具链

  • 构建工具:Vite 5+
  • 代码规范:ESLint 9+ + Prettier 3+
  • Git Hooks:Husky 9+ + lint-staged
  • 测试框架:Vitest + React Testing Library
  • UI 框架:Ant Design 5+ / Arco Design / Headless UI
  • 状态管理:Zustand / Redux Toolkit / Jotai

📖 进阶方向(完成基础后)

  1. 性能优化:React.memo、useMemo、虚拟列表、代码分割、Suspense
  2. TypeScript 深度集成:泛型、类型体操、类型保护
  3. 服务端渲染(SSR):Next.js / Remix
  4. 微前端:qiankun / Module Federation
  5. 测试驱动开发(TDD):单元测试、集成测试、E2E 测试
  6. CI/CD 流水线:GitHub Actions / Jenkins / GitLab CI
  7. 监控与告警:Sentry(错误监控)、Lighthouse(性能监控)

🎯 实战项目建议(循序渐进)

Level 1:Todo List(1-2 天)

  • 目标:熟练使用 useState、useEffect、表单处理
  • 要点:CRUD 操作、本地存储、条件渲染

Level 2:个人博客(3-5 天)

  • 目标:掌握路由、API 请求、列表渲染
  • 要点:文章列表/详情页、分类筛选、搜索功能、Markdown 渲染

Level 3:电商后台管理(1-2 周)

  • 目标:综合运用所有基础知识
  • 要点:表格 CRUD、表单校验、权限控制、状态管理、图表展示

Level 4:在线聊天室(2-3 周)

  • 目标:实时数据处理、复杂状态管理
  • 要点:WebSocket 连接、消息列表、在线状态、文件上传

总结

本文档从资深 React 架构师的视角,系统地梳理了 React 开发的核心知识体系。每个知识点不仅讲解了"是什么"和"怎么用",更重要的是从以下维度进行了深度剖析:

✅ 架构合理性:为什么这样设计?有什么替代方案?
✅ 工程质量:如何写出可维护、可测试的代码?
✅ 性能优化:哪里可能有性能瓶颈?如何优化?
✅ 可扩展性:当前方案能否支撑未来的需求增长?
✅ 安全性:是否存在安全隐患?如何防范?

核心理念

  • 基础扎实:JavaScript/CSS/浏览器原理是地基,不能跳过
  • 理解原理:不只是会用 API,要理解背后的设计思想
  • 最佳实践:参考业界标准(大厂规范),避免踩坑
  • 持续进化:React 生态系统快速发展,保持学习热情

下一步行动建议

  1. 通读本文档,标记不熟悉的知识点
  2. 对照官方文档深入学习
  3. 动手完成 Level 1-2 的实战项目
  4. 加入社区,参与开源项目
  5. 持续练习,形成肌肉记忆

版本:v1.0
最后更新:2026-05-10
适用范围:React 18+ / React Router v6 / Vite 5+
作者视角:基于 5 年+ React 大厂实战经验整理

更多推荐