React 核心技术深度笔记

38 阅读7分钟

React 核心技术深度笔记

一、非交互性更新的优先级

1.1 理解更新优先级

React 18 引入了优先级调度系统,不同类型的更新有不同的优先级:

优先级类型说明
Immediate同步更新需要立即执行,不能中断
UserBlocking用户交互点击、输入等,需要快速响应
Normal普通更新数据获取后的更新
Low低优先级非关键更新,如日志上报
Idle空闲时执行可延迟到浏览器空闲时

1.2 非交互性更新的处理

场景示例

import { startTransition, useEffect } from 'react';

function SearchResults({ query }) {
  const [results, setResults] = useState([]);
  
  useEffect(() => {
    // 低优先级更新:不会阻塞用户交互
    startTransition(() => {
      fetchResults(query).then(data => {
        setResults(data);
      });
    });
  }, [query]);
  
  return <ResultsList items={results} />;
}

使用 useTransition 钩子

import { useTransition } from 'react';

function App() {
  const [isPending, startTransition] = useTransition();
  
  const handleSearch = (query) => {
    // 标记为过渡更新
    startTransition(() => {
      setSearchQuery(query);
    });
  };
  
  return (
    <>
      <SearchInput onChange={handleSearch} />
      {isPending && <LoadingIndicator />}
      <Results query={searchQuery} />
    </>
  );
}

二、Server Components(RSC)深度解析

2.1 什么是 Server Components

定义:在服务端运行的 React 组件,无需发送到客户端

核心优势

  • 📦 减少 JS 体积:不包含在客户端 bundle 中
  • 🚀 数据获取更高效:直接在服务端访问数据库
  • 🎯 SEO 友好:服务端渲染完整 HTML

2.2 RSC vs SSR vs Client Components

特性Server ComponentsSSRClient Components
运行位置服务端服务端渲染,客户端交互客户端
可访问数据库、文件系统仅通过 API浏览器 API
JS 体积0KB需要 hydration包含在 bundle
交互能力需要 hydration完全支持

2.3 RSC 实践

创建 Server Component

// app/PostList.server.jsx
async function PostList() {
  // 直接在服务端获取数据
  const posts = await db.posts.findMany({ 
    orderBy: { createdAt: 'desc' },
    take: 10 
  });
  
  return (
    <ul>
      {posts.map(post => (
        <li key={post.id}>
          <h3>{post.title}</h3>
          <p>{post.excerpt}</p>
        </li>
      ))}
    </ul>
  );
}

混合使用 Server/Client Components

// app/Blog.jsx
import PostList from './PostList.server';
import CommentSection from './CommentSection.client';

function Blog({ postId }) {
  return (
    <div>
      {/* Server Component:渲染内容 */}
      <PostList />
      
      {/* Client Component:处理交互 */}
      <CommentSection postId={postId} />
    </div>
  );
}

2.4 RSC 数据流模式

┌─────────────────────────────────────────────────────────────┐
│                    Server Side                              │
├─────────────────────────────────────────────────────────────┤
│  Server Components                                          │
│  ├─ 获取数据(DB/API)                                      │
│  ├─ 渲染为特殊格式(React Server Component Payload)         │
│  └─ 发送到客户端                                             │
└─────────────────────────────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────────┐
│                    Client Side                              │
├─────────────────────────────────────────────────────────────┤
│  Client Components                                          │
│  ├─ 接收 Server Component 的渲染结果                        │
│  ├─ 进行 hydration(如果需要)                              │
│  └─ 处理用户交互                                            │
└─────────────────────────────────────────────────────────────┘

2.5 RSC 适用场景

内容展示组件:博客文章、产品列表、静态页面
数据密集型组件:仪表盘、报表
SEO 关键页面:首页、落地页

高交互组件:表单、实时编辑器
需要浏览器 API:DOM 操作、事件监听


三、React.memo 完全指南

3.1 什么是 React.memo

定义:高阶组件(HOC),用于对组件输出进行浅比较,避免不必要的重渲染

工作原理

// 伪代码展示 React.memo 的工作方式
function memo(Component) {
  return function MemoizedComponent(props) {
    // 比较前后 props
    if (propsChanged(prevProps, currentProps)) {
      return <Component {...props} />;
    }
    // 返回缓存的结果
    return cachedResult;
  };
}

3.2 使用场景

场景一:纯展示组件

const UserCard = React.memo(function UserCard({ user }) {
  return (
    <div className="card">
      <img src={user.avatar} alt={user.name} />
      <h3>{user.name}</h3>
      <p>{user.bio}</p>
    </div>
  );
});

场景二:列表项组件

const TodoItem = React.memo(function TodoItem({ todo, onToggle }) {
  return (
    <div onClick={() => onToggle(todo.id)}>
      <input 
        type="checkbox" 
        checked={todo.completed} 
        readOnly 
      />
      <span className={todo.completed ? 'completed' : ''}>
        {todo.text}
      </span>
    </div>
  );
});

3.3 自定义比较函数

深度比较场景

const DeepMemoComponent = React.memo(
  MyComponent,
  (prevProps, nextProps) => {
    // 自定义比较逻辑
    return (
      prevProps.id === nextProps.id &&
      prevProps.data.value === nextProps.data.value
    );
  }
);

3.4 React.memo 的性能考量

什么时候使用

  • 组件渲染成本高(复杂计算、大量子组件)
  • 组件频繁接收相同 props
  • 在大型列表中使用

什么时候不使用

  • 简单组件(比较成本 > 渲染成本)
  • props 频繁变化
  • 组件内部有 useState/useContext

3.5 React.memo vs useMemo

特性React.memouseMemo
作用范围组件级值/计算结果
比较方式浅比较 props比较依赖数组
返回值新组件缓存的值
使用方式包装组件包装计算表达式
// React.memo - 缓存组件渲染
const MemoizedList = React.memo(List);

// useMemo - 缓存计算结果
const expensiveValue = useMemo(() => {
  return computeExpensiveData(data);
}, [data]);

四、组件设计原则

4.1 单一职责原则

定义:一个组件应该只负责一件事情

反例

// ❌ 违反单一职责:同时处理数据获取和UI渲染
function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  
  useEffect(() => {
    fetch(`/api/users/${userId}`)
      .then(res => res.json())
      .then(data => setUser(data));
  }, [userId]);
  
  if (!user) return <Loading />;
  
  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
      {/* ... 更多UI */}
    </div>
  );
}

正例

// ✅ 数据获取逻辑
function useUser(userId) {
  const [user, setUser] = useState(null);
  
  useEffect(() => {
    fetch(`/api/users/${userId}`)
      .then(res => res.json())
      .then(data => setUser(data));
  }, [userId]);
  
  return user;
}

// ✅ 纯UI组件
function UserProfile({ user }) {
  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
    </div>
  );
}

// ✅ 使用
function ProfilePage({ userId }) {
  const user = useUser(userId);
  if (!user) return <Loading />;
  return <UserProfile user={user} />;
}

4.2 可复用性原则

设计通用、可配置的组件

示例:通用 Button 组件

const Button = ({ 
  children, 
  variant = 'primary', 
  size = 'md',
  disabled = false,
  onClick,
  className = '',
  ...props 
}) => {
  const baseStyles = 'px-4 py-2 rounded font-medium transition-all';
  
  const variants = {
    primary: 'bg-blue-500 text-white hover:bg-blue-600',
    secondary: 'bg-gray-200 text-gray-800 hover:bg-gray-300',
    danger: 'bg-red-500 text-white hover:bg-red-600',
  };
  
  const sizes = {
    sm: 'text-sm px-3 py-1',
    md: 'text-base',
    lg: 'text-lg px-6 py-3',
  };
  
  return (
    <button
      disabled={disabled}
      onClick={onClick}
      className={`${baseStyles} ${variants[variant]} ${sizes[size]} ${className}`}
      {...props}
    >
      {children}
    </button>
  );
};

// 使用
<Button>默认按钮</Button>
<Button variant="secondary" size="sm">次要按钮</Button>
<Button variant="danger" disabled>危险按钮</Button>

4.3 纯组件原则

定义:相同输入产生相同输出,无副作用

纯组件特征

  • 不修改传入的 props
  • 不依赖外部状态
  • 不产生副作用(如 API 调用、DOM 操作)
  • 相同输入始终返回相同输出

示例

// ✅ 纯组件
function Greeting({ name }) {
  return <h1>Hello, {name}!</h1>;
}

// ❌ 不纯组件
function BadGreeting({ name }) {
  // 副作用:直接修改 props
  name = name.toUpperCase();
  return <h1>Hello, {name}!</h1>;
}

五、React.lazy() 与代码分割

5.1 什么是代码分割

定义:将应用代码分割成多个小块,按需加载

核心价值

  • 📦 减少初始加载体积
  • ⚡ 提升首屏加载速度
  • 🚀 优化用户体验

5.2 React.lazy() 基础用法

import { lazy, Suspense } from 'react';

// 动态导入组件
const HeavyChart = lazy(() => import('./HeavyChart'));
const DataTable = lazy(() => import('./DataTable'));

function Dashboard() {
  return (
    <div>
      <h1>Dashboard</h1>
      <Suspense fallback={<Loading />}>
        <HeavyChart />
        <DataTable />
      </Suspense>
    </div>
  );
}

5.3 路由级代码分割

import { lazy, Suspense } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';

const Home = lazy(() => import('./Home'));
const About = lazy(() => import('./About'));
const Dashboard = lazy(() => import('./Dashboard'));

function App() {
  return (
    <BrowserRouter>
      <Suspense fallback={<LoadingScreen />}>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/about" element={<About />} />
          <Route path="/dashboard" element={<Dashboard />} />
        </Routes>
      </Suspense>
    </BrowserRouter>
  );
}

5.4 高级用法:命名导出

// 导出多个组件
export const Chart = () => <div>Chart</div>;
export const Table = () => <div>Table</div>;

// 导入指定组件
const { Chart } = await import('./components');
// 使用命名导出的 lazy 加载
const Chart = lazy(() => 
  import('./components').then(module => ({ default: module.Chart }))
);

5.5 React.lazy vs loadable-components

特性React.lazyloadable-components
SSR 支持
加载状态需要 Suspense内置 loading 状态
错误处理需要 Error Boundary内置 error 状态
预加载

六、学习阶段指南

6.1 进阶阶段:核心技能

Hooks 精通

  • useState/useEffect/useContext/useReducer
  • useCallback/useMemo/useRef
  • 自定义 Hooks 设计模式

状态管理

  • Context API 深度应用
  • Redux Toolkit 或 Zustand
  • 状态规范化与选择器

路由

  • React Router 6.x 完整掌握
  • 嵌套路由、路由守卫
  • 动态路由与参数处理

项目结构

src/
├── components/          # 通用组件
│   ├── layout/          # 布局组件
│   └── ui/              # UI 原子组件
├── features/            # 功能模块
│   └── auth/            # 认证模块
│       ├── hooks/       # 自定义 hooks
│       ├── components/  # 模块组件
│       └── index.js     # 模块入口
├── hooks/               # 全局 hooks
├── utils/               # 工具函数
└── App.js

6.2 高级阶段:性能优化

渲染优化

  • React.memo、useMemo、useCallback
  • 虚拟列表(react-window/react-virtualized)
  • 状态提升与拆分

资源优化

  • 图片优化(WebP/AVIF)
  • 代码分割与懒加载
  • 资源预加载(preload/prefetch)

服务端渲染

  • Next.js App Router
  • Server Components
  • 数据获取策略(getServerSideProps、fetch)

性能监控

  • Web Vitals
  • React DevTools Profiler
  • 自定义性能指标

6.3 工程化阶段:企业级实践

TypeScript

  • 类型安全、泛型、类型体操
  • 类型定义文件(.d.ts)
  • ESLint + TypeScript 集成

测试体系

  • 单元测试(Jest + RTL)
  • 集成测试(Playwright)
  • 端到端测试

构建工具

  • Vite 配置与插件
  • Webpack 高级配置
  • 构建优化策略

CI/CD

  • GitHub Actions/GitLab CI
  • 自动化测试与部署
  • 环境配置与变量管理

七、最佳实践总结

7.1 性能优化优先级

  1. 架构层面:使用 Server Components 减少客户端 JS
  2. 组件层面:React.memo + useMemo/useCallback 优化渲染
  3. 打包层面:React.lazy + Suspense 实现按需加载
  4. 运行时层面:虚拟滚动、代码分割、资源优化

7.2 组件设计检查清单

  • 是否符合单一职责?
  • 是否可复用、可配置?
  • 是否为纯组件(无副作用)?
  • props 是否最小化、清晰?
  • 是否有合适的错误处理?

7.3 RSC 使用决策树

需要渲染内容吗?
    │
    ├─ 是 → 需要交互吗?
    │       │
    │       ├─ 是 → Client Component
    │       └─ 否 → Server Component
    │
    └─ 否 → 考虑是否需要该组件

这些笔记涵盖了你提到的所有主题,从基础概念到高级实践都有详细说明。建议根据自己的学习阶段,从进阶阶段开始逐步深入,重点关注 Server Components 和性能优化,这是当前 React 生态的核心竞争力所在。