当前位置: 首页 > article >正文

React总结

React 完全指南:从入门到精通

1. React 简介

React 是一个用于构建用户界面的 JavaScript 库,由 Facebook 开发并维护。它具有以下特点:

  • 组件化:将 UI 拆分为独立可复用的组件
  • 声明式:通过 JSX 描述 UI 状态
  • 高效:虚拟 DOM 实现高性能渲染
  • 单向数据流:数据自上而下流动

2. 环境搭建

2.1 使用 Create React App

npx create-react-app my-app
cd my-app
npm start

2.2 项目结构

my-app/
  ├── node_modules/
  ├── public/
  │   └── index.html
  ├── src/
  │   ├── App.css
  │   ├── App.js
  │   ├── index.css
  │   └── index.js
  ├── package.json
  └── README.md

3. 核心概念

3.1 JSX 语法

const element = <h1>Hello, world!</h1>;

3.2 组件

函数组件
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}
类组件
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

3.3 Props 和 State

  • Props:父组件传递给子组件的数据
  • State:组件内部维护的状态
class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

3.4 事件处理

function ActionButton() {
  function handleClick() {
    console.log('Button clicked');
  }

  return (
    <button onClick={handleClick}>
      Click Me
    </button>
  );
}

4. 高级特性

4.1 Hooks

useState
import React, { useState } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}
useEffect
import React, { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

4.2 Context API

const ThemeContext = React.createContext('light');

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <Toolbar />
    </ThemeContext.Provider>
  );
}

function Toolbar() {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

function ThemedButton() {
  const theme = useContext(ThemeContext);
  return <button theme={theme}>I am styled by theme context!</button>;
}

4.3 React Router

import {
  BrowserRouter as Router,
  Switch,
  Route,
  Link
} from "react-router-dom";

function App() {
  return (
    <Router>
      <nav>
        <Link to="/">Home</Link>
        <Link to="/about">About</Link>
      </nav>

      <Switch>
        <Route path="/about">
          <About />
        </Route>
        <Route path="/">
          <Home />
        </Route>
      </Switch>
    </Router>
  );
}

5. 最佳实践

5.1 组件设计原则

  • 单一职责原则
  • 可复用性
  • 可组合性
  • 可测试性

5.2 性能优化

5.2.1 组件优化
  1. React.memo

    const MyComponent = React.memo(function MyComponent(props) {
      // 组件实现
    });
    
  2. PureComponent

    class MyComponent extends React.PureComponent {
      render() {
        return <div>{this.props.value}</div>;
      }
    }
    
  3. shouldComponentUpdate

    class MyComponent extends React.Component {
      shouldComponentUpdate(nextProps, nextState) {
        return nextProps.value !== this.props.value;
      }
      
      render() {
        return <div>{this.props.value}</div>;
      }
    }
    
5.2.2 Hooks优化
  1. useMemo

    const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
    
  2. useCallback

    const memoizedCallback = useCallback(() => {
      doSomething(a, b);
    }, [a, b]);
    
  3. useReducer

    const [state, dispatch] = useReducer(reducer, initialState);
    
5.2.3 代码分割
  1. React.lazy

    const OtherComponent = React.lazy(() => import('./OtherComponent'));
    
    function MyComponent() {
      return (
        <React.Suspense fallback={<div>Loading...</div>}>
          <OtherComponent />
        </React.Suspense>
      );
    }
    
  2. 路由懒加载

    const Home = React.lazy(() => import('./routes/Home'));
    const About = React.lazy(() => import('./routes/About'));
    
    function App() {
      return (
        <Suspense fallback={<div>Loading...</div>}>
          <Routes>
            <Route path="/" element={<Home />} />
            <Route path="/about" element={<About />} />
          </Routes>
        </Suspense>
      );
    }
    
5.2.4 列表优化
  1. 虚拟列表

    import { FixedSizeList as List } from 'react-window';
    
    const Row = ({ index, style }) => (
      <div style={style}>Row {index}</div>
    );
    
    const Example = () => (
      <List
        height={150}
        itemCount={1000}
        itemSize={35}
        width={300}
      >
        {Row}
      </List>
    );
    
  2. 窗口化渲染

    import { VariableSizeList as List } from 'react-window';
    
    const rowHeights = new Array(1000)
      .fill(true)
      .map(() => 25 + Math.round(Math.random() * 50));
    
    const getItemSize = index => rowHeights[index];
    
    const Row = ({ index, style }) => (
      <div style={style}>Row {index}</div>
    );
    
    const Example = () => (
      <List
        height={150}
        itemCount={1000}
        itemSize={getItemSize}
        width={300}
      >
        {Row}
      </List>
    );
    
5.2.5 渲染优化
  1. 批量更新

    import { unstable_batchedUpdates } from 'react-dom';
    
    function handleClick() {
      unstable_batchedUpdates(() => {
        setCount(c => c + 1);
        setFlag(f => !f);
      });
    }
    
  2. 避免不必要的渲染

    function ParentComponent() {
      const [count, setCount] = useState(0);
      
      return (
        <>
          <button onClick={() => setCount(c => c + 1)}>Increment</button>
          <MemoizedChildComponent />
        </>
      );
    }
    
  3. 使用Fragment

    function MyComponent() {
      return (
        <>
          <ChildA />
          <ChildB />
          <ChildC />
        </>
      );
    }
    
5.2.6 内存优化
  1. 清理副作用

    useEffect(() => {
      const subscription = props.source.subscribe();
      return () => {
        subscription.unsubscribe();
      };
    }, [props.source]);
    
  2. 避免内存泄漏

    useEffect(() => {
      let isMounted = true;
      
      async function fetchData() {
        const result = await someAsyncOperation();
        if (isMounted) {
          setData(result);
        }
      }
    
      fetchData();
      
      return () => {
        isMounted = false;
      };
    }, []);
    
  3. 使用WeakMap

    const cache = new WeakMap();
    
    function getCachedData(obj) {
      if (!cache.has(obj)) {
        const result = computeExpensiveValue(obj);
        cache.set(obj, result);
      }
      return cache.get(obj);
    }
    
5.2.7 网络优化
  1. 预加载资源

    import { Prefetch } from 'react-static';
    
    function MyComponent() {
      return (
        <Prefetch path="/about">
          {({ loading, loaded }) => (
            <Link to="/about">
              About {loading ? 'Loading...' : 'Loaded'}
            </Link>
          )}
        </Prefetch>
      );
    }
    
  2. 数据预取

    const { data } = useSWR('/api/data', fetcher, {
      revalidateOnFocus: false,
      revalidateOnReconnect: false
    });
    
  3. 资源优先级

    <link rel="preload" href="critical.css" as="style">
    <link rel="preload" href="main.js" as="script">
    
5.2.8 工具使用
  1. React Profiler

    import { Profiler } from 'react';
    
    function onRenderCallback(
      id,
      phase,
      actualDuration,
      baseDuration,
      startTime,
      commitTime,
      interactions
    ) {
      console.log('Render time:', actualDuration);
    }
    
    function App() {
      return (
        <Profiler id="App" onRender={onRenderCallback}>
          <MyComponent />
        </Profiler>
      );
    }
    
  2. React DevTools

    • 组件性能分析
    • 状态调试
    • 组件树检查
  3. Lighthouse

    • 性能评分
    • 最佳实践检查
    • 可访问性评估

5.3 测试策略

5.3.1 单元测试
  1. 组件测试

    import { render, screen } from '@testing-library/react';
    import Button from './Button';
    
    test('renders button with correct text', () => {
      render(<Button>Click me</Button>);
      const buttonElement = screen.getByText(/click me/i);
      expect(buttonElement).toBeInTheDocument();
    });
    
  2. Hooks测试

    import { renderHook, act } from '@testing-library/react-hooks';
    import useCounter from './useCounter';
    
    test('should increment counter', () => {
      const { result } = renderHook(() => useCounter());
    
      act(() => {
        result.current.increment();
      });
    
      expect(result.current.count).toBe(1);
    });
    
  3. 快照测试

    import renderer from 'react-test-renderer';
    import Component from './Component';
    
    test('matches snapshot', () => {
      const tree = renderer.create(<Component />).toJSON();
      expect(tree).toMatchSnapshot();
    });
    
5.3.2 集成测试
  1. 组件交互测试

    import { render, screen, fireEvent } from '@testing-library/react';
    import LoginForm from './LoginForm';
    
    test('submits form with correct values', () => {
      const handleSubmit = jest.fn();
      render(<LoginForm onSubmit={handleSubmit} />);
    
      fireEvent.change(screen.getByLabelText(/username/i), {
        target: { value: 'testuser' }
      });
    
      fireEvent.change(screen.getByLabelText(/password/i), {
        target: { value: 'password123' }
      });
    
      fireEvent.click(screen.getByRole('button', { name: /submit/i }));
    
      expect(handleSubmit).toHaveBeenCalledWith({
        username: 'testuser',
        password: 'password123'
      });
    });
    
  2. API集成测试

    import { render, screen, waitFor } from '@testing-library/react';
    import axios from 'axios';
    import UserList from './UserList';
    
    jest.mock('axios');
    
    test('fetches and displays users', async () => {
      axios.get.mockResolvedValue({
        data: [{ id: 1, name: 'John Doe' }]
      });
    
      render(<UserList />);
    
      await waitFor(() => {
        expect(screen.getByText(/john doe/i)).toBeInTheDocument();
      });
    });
    
5.3.3 E2E测试
  1. Cypress配置

    // cypress.config.js
    module.exports = {
      e2e: {
        baseUrl: 'http://localhost:3000',
        supportFile: false
      }
    };
    
  2. 页面导航测试

    describe('Navigation', () => {
      it('should navigate to about page', () => {
        cy.visit('/');
        cy.get('a[href*="about"]').click();
        cy.url().should('include', '/about');
        cy.get('h1').contains('About Page');
      });
    });
    
  3. 表单提交测试

    describe('Login Form', () => {
      it('should submit form', () => {
        cy.visit('/login');
        cy.get('#username').type('testuser');
        cy.get('#password').type('password123');
        cy.get('form').submit();
        cy.url().should('include', '/dashboard');
      });
    });
    
5.3.4 测试覆盖率
  1. Jest配置

    {
      "collectCoverage": true,
      "coverageReporters": ["text", "lcov"],
      "coverageThreshold": {
        "global": {
          "branches": 80,
          "functions": 80,
          "lines": 80,
          "statements": 80
        }
      }
    }
    
  2. 生成覆盖率报告

    npm test -- --coverage
    
  3. 查看HTML报告

    open coverage/lcov-report/index.html
    
5.3.5 测试最佳实践
  1. 测试金字塔

    • 70% 单元测试
    • 20% 集成测试
    • 10% E2E测试
  2. 测试命名规范

    • 描述性测试名称
    • Given-When-Then模式
    • 避免使用"should"
  3. 测试数据管理

    • 使用工厂函数生成测试数据
    • 避免硬编码
    • 使用faker.js生成随机数据
  4. 测试隔离

    • 每个测试独立运行
    • 使用beforeEach/afterEach清理状态
    • 避免测试间依赖

6. 生态系统

6.1 状态管理

Redux
  1. 核心概念

    • Store:应用状态容器
    • Action:状态变更描述
    • Reducer:状态变更处理
    • Middleware:扩展功能
  2. 示例代码

    // store.js
    import { createStore } from 'redux';
    
    function counterReducer(state = { value: 0 }, action) {
      switch (action.type) {
        case 'increment':
          return { value: state.value + 1 };
        default:
          return state;
      }
    }
    
    const store = createStore(counterReducer);
    
    // Component.js
    import { useSelector, useDispatch } from 'react-redux';
    
    function Counter() {
      const count = useSelector(state => state.value);
      const dispatch = useDispatch();
    
      return (
        <div>
          <span>{count}</span>
          <button onClick={() => dispatch({ type: 'increment' })}>
            Increment
          </button>
        </div>
      );
    }
    
Recoil
  1. 核心概念

    • Atom:状态单元
    • Selector:派生状态
    • Hooks:状态访问
  2. 示例代码

    // state.js
    import { atom } from 'recoil';
    
    export const countState = atom({
      key: 'countState',
      default: 0,
    });
    
    // Component.js
    import { useRecoilState } from 'recoil';
    import { countState } from './state';
    
    function Counter() {
      const [count, setCount] = useRecoilState(countState);
    
      return (
        <div>
          <span>{count}</span>
          <button onClick={() => setCount(count + 1)}>
            Increment
          </button>
        </div>
      );
    }
    

6.2 样式方案

Styled Components
  1. 基本用法

    import styled from 'styled-components';
    
    const Button = styled.button`
      background: ${props => props.primary ? 'palevioletred' : 'white'};
      color: ${props => props.primary ? 'white' : 'palevioletred'};
      font-size: 1em;
      padding: 0.25em 1em;
      border: 2px solid palevioletred;
      border-radius: 3px;
    `;
    
    function Example() {
      return (
        <div>
          <Button>Normal</Button>
          <Button primary>Primary</Button>
        </div>
      );
    }
    
  2. 主题支持

    import { ThemeProvider } from 'styled-components';
    
    const theme = {
      colors: {
        primary: 'palevioletred',
        secondary: 'white'
      }
    };
    
    function App() {
      return (
        <ThemeProvider theme={theme}>
          <Button>Styled</Button>
        </ThemeProvider>
      );
    }
    
CSS Modules
  1. 基本用法

    /* Button.module.css */
    .button {
      background-color: white;
      color: palevioletred;
      font-size: 1em;
      padding: 0.25em 1em;
      border: 2px solid palevioletred;
      border-radius: 3px;
    }
    
    import styles from './Button.module.css';
    
    function Button() {
      return (
        <button className={styles.button}>
          Click me
        </button>
      );
    }
    
  2. 组合样式

    .primary {
      background-color: palevioletred;
      color: white;
    }
    
    function Button({ primary }) {
      return (
        <button className={`${styles.button} ${primary && styles.primary}`}>
          Click me
        </button>
      );
    }
    

6.3 表单处理

Formik
  1. 基本用法

    import { Formik, Form, Field } from 'formik';
    
    function LoginForm() {
      return (
        <Formik
          initialValues={{ email: '', password: '' }}
          onSubmit={values => {
            console.log(values);
          }}
        >
          <Form>
            <Field name="email" type="email" />
            <Field name="password" type="password" />
            <button type="submit">Submit</button>
          </Form>
        </Formik>
      );
    }
    
  2. 表单验证

    function validate(values) {
      const errors = {};
      if (!values.email) {
        errors.email = 'Required';
      } else if (
        !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email)
      ) {
        errors.email = 'Invalid email address';
      }
      return errors;
    }
    
React Hook Form
  1. 基本用法

    import { useForm } from 'react-hook-form';
    
    function LoginForm() {
      const { register, handleSubmit } = useForm();
      const onSubmit = data => console.log(data);
    
      return (
        <form onSubmit={handleSubmit(onSubmit)}>
          <input {...register('email')} />
          <input {...register('password')} type="password" />
          <button type="submit">Submit</button>
        </form>
      );
    }
    
  2. 表单验证

    <input
      {...register('email', {
        required: 'Email is required',
        pattern: {
          value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
          message: 'Invalid email address'
        }
      })}
    />
    

6.4 数据获取

React Query
  1. 基本用法

    import { useQuery } from 'react-query';
    
    function UserList() {
      const { data, isLoading, error } = useQuery('users', () =>
        fetch('/api/users').then(res => res.json())
      );
    
      if (isLoading) return 'Loading...';
      if (error) return 'Error!';
    
      return (
        <ul>
          {data.map(user => (
            <li key={user.id}>{user.name}</li>
          ))}
        </ul>
      );
    }
    
  2. 数据缓存

    const { data } = useQuery('users', fetchUsers, {
      staleTime: 1000 * 60 * 5, // 5 minutes
      cacheTime: 1000 * 60 * 10 // 10 minutes
    });
    
SWR
  1. 基本用法

    import useSWR from 'swr';
    
    function Profile() {
      const { data, error } = useSWR('/api/user', fetcher);
    
      if (error) return <div>failed to load</div>;
      if (!data) return <div>loading...</div>;
      return <div>hello {data.name}!</div>;
    }
    
  2. 自动重试

    const { data } = useSWR('/api/user', fetcher, {
      onErrorRetry: (error, key, config, revalidate, { retryCount }) => {
        if (error.status === 404) return;
        if (retryCount >= 10) return;
        setTimeout(() => revalidate({ retryCount }), 5000);
      }
    });
    

6.5 UI 库

Material-UI
  1. 基本用法

    import { Button, TextField } from '@mui/material';
    
    function Form() {
      return (
        <form>
          <TextField label="Email" variant="outlined" />
          <TextField label="Password" type="password" variant="outlined" />
          <Button variant="contained" color="primary">
            Submit
          </Button>
        </form>
      );
    }
    
  2. 主题定制

    import { createTheme, ThemeProvider } from '@mui/material/styles';
    
    const theme = createTheme({
      palette: {
        primary: {
          main: '#1976d2',
        },
      },
    });
    
    function App() {
      return (
        <ThemeProvider theme={theme}>
          <Form />
        </ThemeProvider>
      );
    }
    
Ant Design
  1. 基本用法

    import { Button, Input } from 'antd';
    
    function Form() {
      return (
        <form>
          <Input placeholder="Email" />
          <Input.Password placeholder="Password" />
          <Button type="primary">Submit</Button>
        </form>
      );
    }
    
  2. 主题定制

    import { ConfigProvider } from 'antd';
    
    function App() {
      return (
        <ConfigProvider
          theme={{
            token: {
              colorPrimary: '#00b96b',
            },
          }}
        >
          <Form />
        </ConfigProvider>
      );
    }
    

7. 学习资源

  • 官方文档:https://reactjs.org/
  • React 中文文档:https://zh-hans.reactjs.org/
  • React 入门教程:https://react.iamkasong.com/
  • React 进阶指南:https://react.iamkasong.com/advanced/
  • React 源码解析:https://react.iamkasong.com/implementation/

8. React 18 新特性

8.1 并发渲染(Concurrent Rendering)

  • 可中断渲染:允许React在渲染过程中暂停和恢复
  • 自动批处理:自动合并多个状态更新
  • 过渡更新:区分紧急和非紧急更新
import { startTransition } from 'react';

// 紧急更新
setInputValue(input);

// 非紧急更新
startTransition(() => {
  setSearchQuery(input);
});

8.2 新的Hooks

  • useId:生成唯一ID
  • useSyncExternalStore:与外部存储同步
  • useInsertionEffect:用于CSS-in-JS库

8.3 服务端渲染改进

  • 流式SSR:支持渐进式HTML流
  • 选择性水合:优先水合重要部分

8.4 其他改进

  • 新的根API:createRoot
  • 严格模式增强:检测不安全的副作用
  • 改进的错误处理:自动恢复错误边界

9. Server Components

9.1 概念与优势

  • 在服务端渲染组件
  • 减少客户端包体积
  • 直接访问后端服务
  • 自动代码分割

9.2 使用场景

  • 数据获取密集型组件
  • 静态内容
  • 大型依赖组件
  • 安全性要求高的组件

9.3 示例代码

// Note.server.js
import db from 'db.server';

function Note({id}) {
  const note = db.notes.get(id);
  return <NoteView note={note} />;
}

// NoteView.client.js
'use client';

function NoteView({note}) {
  return (
    <div>
      <h1>{note.title}</h1>
      <p>{note.content}</p>
    </div>
  );
}

9.4 注意事项

  • 客户端交互限制
  • 状态管理方式
  • 网络延迟影响
  • 开发环境配置

10. 状态管理库对比

10.1 Redux

  • 优点:
    • 成熟的生态系统
    • 强大的中间件支持
    • 时间旅行调试
    • 严格的单向数据流
  • 缺点:
    • 样板代码较多
    • 学习曲线较陡
    • 可能过度设计简单场景

10.2 Recoil

  • 优点:
    • 原生支持React
    • 细粒度状态管理
    • 异步数据支持
    • 更简单的API
  • 缺点:
    • 相对较新
    • 生态系统不够成熟
    • 调试工具有限

10.3 Zustand

  • 优点:
    • 极简API
    • 高性能
    • 支持中间件
    • 易于集成
  • 缺点:
    • 功能相对基础
    • 社区支持较少
    • 缺少内置异步处理

10.4 选择建议

场景推荐方案
大型复杂应用Redux
中小型应用Recoil
简单应用Zustand
需要时间旅行调试Redux
需要细粒度更新Recoil
追求极简实现Zustand

11. 常见问题与解决方案

11.1 性能问题

  • 问题:组件频繁重新渲染
    • 解决方案:使用React.memo、useMemo、useCallback优化
  • 问题:长列表渲染卡顿
    • 解决方案:使用虚拟列表库(如react-window)

11.2 状态管理

  • 问题:组件间状态共享困难
    • 解决方案:使用Context API或状态管理库
  • 问题:状态更新导致意外渲染
    • 解决方案:检查依赖数组,使用useEffect正确管理副作用

11.3 路由问题

  • 问题:页面刷新后404
    • 解决方案:配置服务器支持HTML5 History API
  • 问题:路由切换时数据丢失
    • 解决方案:使用状态管理或路由缓存

11.4 样式冲突

  • 问题:全局样式污染
    • 解决方案:使用CSS Modules或CSS-in-JS
  • 问题:第三方组件样式覆盖
    • 解决方案:使用CSS命名空间或样式优先级调整

11.5 开发环境

  • 问题:热更新失效
    • 解决方案:检查webpack配置,确保HMR启用
  • 问题:TypeScript类型错误
    • 解决方案:安装正确的类型定义文件(@types包)

12. 项目架构设计

12.1 分层架构

展示层(Presentation Layer)
  1. 职责

    • UI展示
    • 用户交互
    • 数据展示
    • 样式管理
  2. 技术栈

    • React组件
    • CSS-in-JS
    • UI库(如Material-UI)
    • 动画库(如Framer Motion)
  3. 示例代码

    // components/Button.jsx
    import styled from 'styled-components';
    
    const StyledButton = styled.button`
      background: ${props => props.primary ? 'blue' : 'white'};
      color: ${props => props.primary ? 'white' : 'blue'};
      padding: 0.5rem 1rem;
      border-radius: 4px;
      cursor: pointer;
    `;
    
    function Button({ primary, children }) {
      return <StyledButton primary={primary}>{children}</StyledButton>;
    }
    
业务逻辑层(Business Logic Layer)
  1. 职责

    • 业务规则处理
    • 数据转换
    • 状态管理
    • 业务逻辑复用
  2. 技术栈

    • 自定义Hooks
    • 服务类
    • 状态管理库(如Redux、Recoil)
    • 数据验证库(如Yup)
  3. 示例代码

    // hooks/useForm.js
    import { useState } from 'react';
    
    export function useForm(initialValues) {
      const [values, setValues] = useState(initialValues);
    
      const handleChange = (e) => {
        const { name, value } = e.target;
        setValues({
          ...values,
          [name]: value
        });
      };
    
      return {
        values,
        handleChange
      };
    }
    
数据访问层(Data Access Layer)
  1. 职责

    • API调用
    • 数据缓存
    • 错误处理
    • 数据转换
  2. 技术栈

    • Axios
    • React Query
    • GraphQL客户端(如Apollo)
    • WebSocket
  3. 示例代码

    // services/api.js
    import axios from 'axios';
    
    const api = axios.create({
      baseURL: process.env.REACT_APP_API_URL,
      timeout: 10000,
      headers: {
        'Content-Type': 'application/json'
      }
    });
    
    export const getUsers = async () => {
      try {
        const response = await api.get('/users');
        return response.data;
      } catch (error) {
        throw new Error(error.response?.data?.message || 'Failed to fetch users');
      }
    };
    

12.2 目录结构

基础结构
src/
├── assets/          # 静态资源
│   ├── images/      # 图片资源
│   ├── fonts/       # 字体文件
│   └── styles/      # 全局样式
├── components/      # 通用组件
│   ├── ui/          # UI组件
│   ├── layout/      # 布局组件
│   └── shared/      # 共享组件
├── features/        # 功能模块
│   └── featureName/
│       ├── api/     # API接口
│       ├── hooks/   # 自定义Hooks
│       ├── store/   # 状态管理
│       ├── types/   # 类型定义
│       └── views/   # 页面组件
├── lib/             # 工具函数
│   ├── utils/       # 通用工具
│   ├── constants/   # 常量定义
│   └── helpers/     # 辅助函数
├── pages/           # 页面路由
│   ├── Home/        # 首页
│   ├── About/       # 关于页
│   └── NotFound/    # 404页
├── services/        # API服务
│   ├── api.js       # API配置
│   ├── auth.js      # 认证服务
│   └── storage.js   # 存储服务
├── store/           # 全局状态
│   ├── slices/      # Redux切片
│   ├── actions/     # Redux动作
│   └── reducers/    # Redux reducer
├── styles/          # 全局样式
│   ├── theme.js     # 主题配置
│   ├── global.css   # 全局样式
│   └── variables.css # CSS变量
└── utils/           # 工具函数
    ├── validators/  # 验证工具
    ├── formatters/  # 格式化工具
    └── hooks/       # 自定义Hooks
模块化结构
src/
├── modules/
│   ├── auth/        # 认证模块
│   │   ├── api/
│   │   ├── components/
│   │   ├── hooks/
│   │   ├── store/
│   │   └── views/
│   ├── user/        # 用户模块
│   │   ├── api/
│   │   ├── components/
│   │   ├── hooks/
│   │   ├── store/
│   │   └── views/
│   └── product/     # 产品模块
│       ├── api/
│       ├── components/
│       ├── hooks/
│       ├── store/
│       └── views/

12.3 模块化设计

模块划分原则
  1. 按业务功能划分

    • 用户管理
      • 用户注册/登录
      • 用户信息管理
      • 权限控制
    • 订单管理
      • 订单创建
      • 订单查询
      • 订单状态管理
    • 产品管理
      • 产品展示
      • 产品分类
      • 产品搜索
    • 权限管理
      • 角色管理
      • 权限分配
      • 访问控制
  2. 按技术特性划分

    • 认证模块
      • 登录/注册
      • 会话管理
      • 权限验证
    • 支付模块
      • 支付接口
      • 支付状态管理
      • 支付回调处理
    • 通知模块
      • 消息推送
      • 邮件通知
      • 站内信
    • 日志模块
      • 操作日志
      • 错误日志
      • 访问日志
  3. 模块间通信

    • 通过props传递数据
      // ParentComponent.jsx
      function ParentComponent() {
        const [data, setData] = useState(null);
        return <ChildComponent data={data} onDataChange={setData} />;
      }
      
      // ChildComponent.jsx
      function ChildComponent({ data, onDataChange }) {
        return (
          <input 
            value={data || ''}
            onChange={(e) => onDataChange(e.target.value)}
          />
        );
      }
      
    • 使用Context API共享状态
      // ThemeContext.js
      const ThemeContext = createContext();
      
      function ThemeProvider({ children }) {
        const [theme, setTheme] = useState('light');
        return (
          <ThemeContext.Provider value={{ theme, setTheme }}>
            {children}
          </ThemeContext.Provider>
        );
      }
      
      // ThemedButton.jsx
      function ThemedButton() {
        const { theme, setTheme } = useContext(ThemeContext);
        return (
          <button 
            style={{ backgroundColor: theme === 'light' ? '#fff' : '#333' }}
            onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}
          >
            Toggle Theme
          </button>
        );
      }
      
    • 通过事件总线通信
      // eventBus.js
      class EventBus {
        constructor() {
          this.events = {};
        }
      
        on(event, callback) {
          if (!this.events[event]) {
            this.events[event] = [];
          }
          this.events[event].push(callback);
        }
      
        emit(event, ...args) {
          if (this.events[event]) {
            this.events[event].forEach(callback => callback(...args));
          }
        }
      }
      
      export default new EventBus();
      
    • 使用状态管理库
      // store.js
      import { configureStore } from '@reduxjs/toolkit';
      import authReducer from './authSlice';
      import userReducer from './userSlice';
      
      export default configureStore({
        reducer: {
          auth: authReducer,
          user: userReducer
        }
      });
      
模块接口设计
  1. 组件接口

    // modules/auth/components/LoginForm.jsx
    import PropTypes from 'prop-types';
    
    function LoginForm({ onSubmit, loading, error }) {
      return (
        <form onSubmit={onSubmit}>
          {error && <div className="error">{error}</div>}
          <input name="email" type="email" required />
          <input name="password" type="password" required />
          <button type="submit" disabled={loading}>
            {loading ? 'Loading...' : 'Login'}
          </button>
        </form>
      );
    }
    
    LoginForm.propTypes = {
      onSubmit: PropTypes.func.isRequired,
      loading: PropTypes.bool,
      error: PropTypes.string
    };
    
    LoginForm.defaultProps = {
      loading: false,
      error: null
    };
    
  2. API接口

    // modules/auth/api/auth.js
    import axios from 'axios';
    
    const API_BASE_URL = process.env.REACT_APP_API_URL;
    
    export const login = async (credentials) => {
      try {
        const response = await axios.post(`${API_BASE_URL}/auth/login`, credentials);
        return response.data;
      } catch (error) {
        throw new Error(error.response?.data?.message || 'Login failed');
      }
    };
    
    export const register = async (userData) => {
      try {
        const response = await axios.post(`${API_BASE_URL}/auth/register`, userData);
        return response.data;
      } catch (error) {
        throw new Error(error.response?.data?.message || 'Registration failed');
      }
    };
    
    export const logout = async () => {
      try {
        await axios.post(`${API_BASE_URL}/auth/logout`);
      } catch (error) {
        throw new Error(error.response?.data?.message || 'Logout failed');
      }
    };
    
  3. 状态接口

    // modules/auth/store/authSlice.js
    import { createSlice } from '@reduxjs/toolkit';
    
    const initialState = {
      user: null,
      loading: false,
      error: null
    };
    
    const authSlice = createSlice({
      name: 'auth',
      initialState,
      reducers: {
        loginStart(state) {
          state.loading = true;
          state.error = null;
        },
        loginSuccess(state, action) {
          state.user = action.payload;
          state.loading = false;
        },
        loginFailure(state, action) {
          state.error = action.payload;
          state.loading = false;
        },
        logoutSuccess(state) {
          state.user = null;
        }
      }
    });
    
    export const { loginStart, loginSuccess, loginFailure, logoutSuccess } = authSlice.actions;
    
    export const login = (credentials) => async (dispatch) => {
      try {
        dispatch(loginStart());
        const user = await loginAPI(credentials);
        dispatch(loginSuccess(user));
      } catch (error) {
        dispatch(loginFailure(error.message));
      }
    };
    
    export const logout = () => async (dispatch) => {
      try {
        await logoutAPI();
        dispatch(logoutSuccess());
      } catch (error) {
        console.error('Logout error:', error);
      }
    };
    
    export default authSlice.reducer;
    
  4. 类型接口(TypeScript)

    // modules/auth/types.ts
    export interface User {
      id: string;
      name: string;
      email: string;
      role: string;
    }
    
    export interface AuthState {
      user: User | null;
      loading: boolean;
      error: string | null;
    }
    
    export interface LoginCredentials {
      email: string;
      password: string;
    }
    
    export interface RegisterData extends LoginCredentials {
      name: string;
    }
    
模块依赖管理
  1. 模块间依赖

    • 使用依赖注入
      // modules/auth/authService.js
      class AuthService {
        constructor(apiClient) {
          this.apiClient = apiClient;
        }
      
        async login(credentials) {
          return this.apiClient.post('/auth/login', credentials);
        }
      }
      
      // main.js
      const apiClient = new ApiClient();
      const authService = new AuthService(apiClient);
      
  2. 模块版本控制

    • 使用语义化版本
      {
        "dependencies": {
          "@modules/auth": "^1.2.0",
          "@modules/user": "~2.0.1"
        }
      }
      
  3. 模块更新策略

    • 向后兼容
    • 版本迁移指南
    • 废弃警告
模块测试策略
  1. 单元测试

    // modules/auth/authSlice.test.js
    import authReducer, { loginStart, loginSuccess, loginFailure } from './authSlice';
    
    describe('auth reducer', () => {
      it('should handle initial state', () => {
        expect(authReducer(undefined, {})).toEqual({
          user: null,
          loading: false,
          error: null
        });
      });
    
      it('should handle loginStart', () => {
        expect(
          authReducer(undefined, loginStart())
        ).toEqual({
          user: null,
          loading: true,
          error: null
        });
      });
    });
    
  2. 集成测试

    // modules/auth/authService.test.js
    import AuthService from './authService';
    import MockAdapter from 'axios-mock-adapter';
    import axios from 'axios';
    
    describe('AuthService', () => {
      let mockAxios;
      let authService;
    
      beforeEach(() => {
        mockAxios = new MockAdapter(axios);
        authService = new AuthService(axios);
      });
    
      it('should login successfully', async () => {
        const credentials = { email: 'test@example.com', password: 'password' };
        const responseData = { token: 'abc123' };
    
        mockAxios.onPost('/auth/login').reply(200, responseData);
    
        const result = await authService.login(credentials);
        expect(result.data).toEqual(responseData);
      });
    });
    
  3. E2E测试

    // cypress/integration/auth.spec.js
    describe('Authentication', () => {
      it('should login successfully', () => {
        cy.visit('/login');
        cy.get('[name="email"]').type('test@example.com');
        cy.get('[name="password"]').type('password');
        cy.get('form').submit();
        cy.url().should('include', '/dashboard');
      });
    });
    
模块文档规范
  1. README模板

    # Auth Module
    
    ## 功能描述
    - 用户登录/注册
    - 会话管理
    - 权限验证
    
    ## 安装
    ```bash
    npm install @modules/auth
    

    使用示例

    import { login } from '@modules/auth';
    
    const credentials = {
      email: 'test@example.com',
      password: 'password123'
    };
    
    login(credentials)
      .then(user => console.log('Logged in:', user))
      .catch(error => console.error('Login failed:', error));
    

    API 文档

    • login(credentials)
    • register(userData)
    • logout()

    依赖

    • axios
    • redux
    
    
  2. API文档生成

    • 使用JSDoc
      /**
       * 用户登录
       * @param {Object} credentials - 登录凭证
       * @param {string} credentials.email - 用户邮箱
       * @param {string} credentials.password - 用户密码
       * @returns {Promise<User>} 返回用户信息
       * @throws {Error} 登录失败时抛出错误
       */
      export async function login(credentials) {
        // ...
      }
      
  3. 变更日志

    # Changelog
    
    ## [1.2.0] - 2023-03-15
    ### Added
    - 新增第三方登录支持
    
    ### Changed
    - 优化登录错误提示
    
    ### Fixed
    - 修复会话过期问题
    
模块发布流程
  1. 版本管理

    npm version patch
    npm version minor
    npm version major
    
  2. 发布到私有仓库

    npm publish --access restricted
    
  3. 自动更新

    {
      "scripts": {
        "postpublish": "git push && git push --tags"
      }
    }
    
模块最佳实践
  1. 单一职责原则

    • 每个模块只负责一个功能
    • 避免过度耦合
  2. 接口最小化

    • 只暴露必要的接口
    • 使用私有方法
  3. 依赖管理

    • 最小化外部依赖
    • 使用peerDependencies
  4. 性能优化

    • 懒加载模块
    • 代码分割
  5. 安全性

    • 输入验证
    • 权限控制
    • 错误处理

12.4 性能优化策略

代码分割
  1. 动态导入

    const LazyComponent = React.lazy(() => import('./LazyComponent'));
    
    function App() {
      return (
        <React.Suspense fallback={<div>Loading...</div>}>
          <LazyComponent />
        </React.Suspense>
      );
    }
    
  2. 路由懒加载

    const Home = React.lazy(() => import('./pages/Home'));
    const About = React.lazy(() => import('./pages/About'));
    
    function App() {
      return (
        <Suspense fallback={<div>Loading...</div>}>
          <Routes>
            <Route path="/" element={<Home />} />
            <Route path="/about" element={<About />} />
          </Routes>
        </Suspense>
      );
    }
    
  3. 组件级代码分割

    const HeavyComponent = React.lazy(() => 
      import('./HeavyComponent').then(module => ({
        default: module.HeavyComponent
      }))
    );
    
  4. 预加载策略

    const preloadComponent = (component) => {
      const Component = React.lazy(component);
      Component.preload();
    };
    
    // 鼠标悬停时预加载
    <div onMouseEnter={() => preloadComponent(() => import('./HeavyComponent'))}>
      Hover to preload
    </div>
    
资源优化
  1. 图片优化

    • 使用WebP格式
      <picture>
        <source srcSet="image.webp" type="image/webp" />
        <source srcSet="image.jpg" type="image/jpeg" /> 
        <img src="image.jpg" alt="Example" />
      </picture>
      
    • 响应式图片
      <img
        srcSet="image-320w.jpg 320w,
                image-480w.jpg 480w,
                image-800w.jpg 800w"
        sizes="(max-width: 320px) 280px,
               (max-width: 480px) 440px,
               800px"
        src="image-800w.jpg"
        alt="Example"
      />
      
    • 图片懒加载
      <img
        src="placeholder.jpg"
        data-src="real-image.jpg"
        className="lazyload"
        alt="Example"
      />
      
  2. 字体优化

    • 字体子集化
      @font-face {
        font-family: 'CustomFont';
        src: url('/fonts/custom-subset.woff2') format('woff2'),
             url('/fonts/custom-subset.woff') format('woff');
        unicode-range: U+000-5FF; /* 仅包含常用字符 */
      }
      
    • 字体预加载
      <link 
        rel="preload" 
        href="/fonts/custom.woff2" 
        as="font" 
        type="font/woff2" 
        crossorigin
      />
      
    • 字体显示策略
      @font-face {
        font-display: swap; /* 使用系统字体直到自定义字体加载完成 */
      }
      
  3. 资源压缩

    • Webpack配置示例
      module.exports = {
        optimization: {
          minimize: true,
          minimizer: [
            new TerserPlugin({
              parallel: true,
              terserOptions: {
                compress: true,
                mangle: true
              }
            }),
            new CssMinimizerPlugin()
          ]
        }
      };
      
缓存策略
  1. Service Worker

    • 缓存策略配置
      // service-worker.js
      const CACHE_NAME = 'v1';
      const ASSETS = [
        '/',
        '/index.html',
        '/main.js',
        '/style.css'
      ];
      
      self.addEventListener('install', (event) => {
        event.waitUntil(
          caches.open(CACHE_NAME)
            .then(cache => cache.addAll(ASSETS))
        );
      });
      
      self.addEventListener('fetch', (event) => {
        event.respondWith(
          caches.match(event.request)
            .then(response => response || fetch(event.request))
        );
      });
      
    • 缓存更新策略
      self.addEventListener('activate', (event) => {
        const cacheWhitelist = [CACHE_NAME];
        event.waitUntil(
          caches.keys().then(cacheNames => {
            return Promise.all(
              cacheNames.map(cacheName => {
                if (!cacheWhitelist.includes(cacheName)) {
                  return caches.delete(cacheName);
                }
              })
            );
          })
        );
      });
      
  2. HTTP缓存

    • Nginx配置
      location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
        add_header Vary "Accept-Encoding";
      }
      
    • ETag配置
      etag on;
      
  3. 数据缓存

    • React Query缓存配置
      const queryClient = new QueryClient({
        defaultOptions: {
          queries: {
            staleTime: 1000 * 60 * 5, // 5分钟
            cacheTime: 1000 * 60 * 10 // 10分钟
          }
        }
      });
      
渲染优化
  1. 虚拟列表

    • 固定高度列表
      import { FixedSizeList as List } from 'react-window';
      
      const Row = ({ index, style }) => (
        <div style={style}>Row {index}</div>
      );
      
      const Example = () => (
        <List
          height={150}
          itemCount={1000}
          itemSize={35}
          width={300}
        >
          {Row}
        </List>
      );
      
    • 可变高度列表
      import { VariableSizeList as List } from 'react-window';
      
      const rowHeights = new Array(1000)
        .fill(true)
        .map(() => 25 + Math.round(Math.random() * 50));
      
      const getItemSize = index => rowHeights[index];
      
      const Row = ({ index, style }) => (
        <div style={style}>Row {index}</div>
      );
      
      const Example = () => (
        <List
          height={150}
          itemCount={1000}
          itemSize={getItemSize}
          width={300}
        >
          {Row}
        </List>
      );
      
  2. 批量更新

    • React 18自动批处理
      function handleClick() {
        setCount(c => c + 1);
        setFlag(f => !f); // 自动批处理
      }
      
    • 手动批处理
      import { unstable_batchedUpdates } from 'react-dom';
      
      function handleClick() {
        unstable_batchedUpdates(() => {
          setCount(c => c + 1);
          setFlag(f => !f);
        });
      }
      
  3. 渲染优先级

    • 使用startTransition
      import { startTransition } from 'react';
      
      function handleInputChange(value) {
        setInputValue(value); // 紧急更新
        startTransition(() => {
          setSearchQuery(value); // 非紧急更新
        });
      }
      
  4. 避免不必要的渲染

    • 使用React.memo
      const MemoizedComponent = React.memo(function Component({ data }) {
        return <div>{data}</div>;
      });
      
    • 使用useMemo
      const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
      
    • 使用useCallback
      const memoizedCallback = useCallback(() => {
        doSomething(a, b);
      }, [a, b]);
      
网络优化
  1. HTTP/2配置

    • Nginx配置
      server {
        listen 443 ssl http2;
        http2_push /style.css;
        http2_push /app.js;
      }
      
  2. 资源预加载

    • 关键资源预加载
      <link rel="preload" href="critical.css" as="style">
      <link rel="preload" href="main.js" as="script">
      
    • 数据预取
      const { data } = useSWR('/api/data', fetcher, {
        revalidateOnFocus: false,
        revalidateOnReconnect: false
      });
      
  3. 服务端推送

    • 使用HTTP/2 Server Push
    • 缓存策略优化
      location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
      }
      
调试技巧
  1. React Profiler

    • 性能分析
      <Profiler id="App" onRender={onRenderCallback}>
        <App />
      </Profiler>
      
    • 自定义回调
      function onRenderCallback(
        id,
        phase,
        actualDuration,
        baseDuration,
        startTime,
        commitTime,
        interactions
      ) {
        console.log('Render time:', actualDuration);
      }
      
  2. Chrome DevTools

    • 性能面板
    • 内存面板
    • 网络面板
  3. Lighthouse

    • 性能评分
    • 最佳实践检查
    • 可访问性评估
性能监控
  1. Web Vitals

    • 核心指标监控
      import { getCLS, getFID, getLCP } from 'web-vitals';
      
      getCLS(console.log);
      getFID(console.log); 
      getLCP(console.log);
      
    • 自定义上报
      function sendToAnalytics(metric) {
        const body = JSON.stringify(metric);
        navigator.sendBeacon('/analytics', body);
      }
      
      getCLS(sendToAnalytics);
      getFID(sendToAnalytics);
      getLCP(sendToAnalytics);
      
  2. 错误监控

    • 错误边界
      class ErrorBoundary extends React.Component {
        state = { hasError: false };
      
        static getDerivedStateFromError(error) {
          return { hasError: true };
        }
      
        componentDidCatch(error, errorInfo) {
          logErrorToService(error, errorInfo);
        }
      
        render() {
          if (this.state.hasError) {
            return <h1>Something went wrong.</h1>;
          }
      
          return this.props.children; 
        }
      }
      
    • 全局错误捕获
      window.addEventListener('error', (event) => {
        logErrorToService(event.error);
      });
      
      window.addEventListener('unhandledrejection', (event) => {
        logErrorToService(event.reason);
      });
      
性能优化最佳实践
  1. 关键渲染路径优化

    • 最小化关键资源
    • 减少关键请求数量
    • 优化关键字节数
  2. 资源加载策略

    • 预加载关键资源
    • 延迟加载非关键资源
    • 按需加载第三方库
  3. 渲染性能优化

    • 避免不必要的重新渲染
    • 使用虚拟列表
    • 优化复杂组件
  4. 网络性能优化

    • 使用HTTP/2
    • 启用Gzip压缩
    • 使用CDN加速
  5. 内存管理

    • 清理未使用的引用
    • 避免内存泄漏
    • 使用WeakMap/WeakSet
  6. 工具使用

    • React DevTools
    • Chrome DevTools
    • Lighthouse
    • Webpack Bundle Analyzer

12.5 测试策略

测试金字塔
  1. 单元测试

    • 测试单个组件或函数
    • 快速执行
    • 高覆盖率
  2. 集成测试

    • 测试组件间交互
    • 验证数据流
    • 确保模块协同工作
  3. E2E测试

    • 模拟用户操作
    • 验证完整功能
    • 确保用户体验
单元测试
  1. 组件测试

    • 渲染测试
      import { render, screen } from '@testing-library/react';
      import Button from './Button';
      
      test('renders button with correct text', () => {
        render(<Button>Click me</Button>);
        const buttonElement = screen.getByText(/click me/i);
        expect(buttonElement).toBeInTheDocument();
      });
      
    • 交互测试
      test('calls onClick handler when clicked', () => {
        const handleClick = jest.fn();
        render(<Button onClick={handleClick}>Click me</Button>);
        
        fireEvent.click(screen.getByRole('button'));
        expect(handleClick).toHaveBeenCalledTimes(1);
      });
      
  2. Hooks测试

    • 状态测试
      import { renderHook, act } from '@testing-library/react-hooks';
      import useCounter from './useCounter';
      
      test('should increment counter', () => {
        const { result } = renderHook(() => useCounter());
      
        act(() => {
          result.current.increment();
        });
      
        expect(result.current.count).toBe(1);
      });
      
    • 副作用测试
      test('should update document title', () => {
        const { result } = renderHook(() => useDocumentTitle('Test Title'));
        
        expect(document.title).toBe('Test Title');
      });
      
  3. 快照测试

    • 组件结构验证
      import renderer from 'react-test-renderer';
      import Component from './Component';
      
      test('matches snapshot', () => {
        const tree = renderer.create(<Component />).toJSON();
        expect(tree).toMatchSnapshot();
      });
      
集成测试
  1. 组件交互测试

    • 表单提交测试
      import { render, screen, fireEvent } from '@testing-library/react';
      import LoginForm from './LoginForm';
      
      test('submits form with correct values', () => {
        const handleSubmit = jest.fn();
        render(<LoginForm onSubmit={handleSubmit} />);
      
        fireEvent.change(screen.getByLabelText(/username/i), {
          target: { value: 'testuser' }
        });
      
        fireEvent.change(screen.getByLabelText(/password/i), {
          target: { value: 'password123' }
        });
      
        fireEvent.click(screen.getByRole('button', { name: /submit/i }));
      
        expect(handleSubmit).toHaveBeenCalledWith({
          username: 'testuser',
          password: 'password123'
        });
      });
      
  2. API集成测试

    • 模拟API请求
      import { render, screen, waitFor } from '@testing-library/react';
      import axios from 'axios';
      import UserList from './UserList';
      
      jest.mock('axios');
      
      test('fetches and displays users', async () => {
        axios.get.mockResolvedValue({
          data: [{ id: 1, name: 'John Doe' }]
        });
      
        render(<UserList />);
      
        await waitFor(() => {
          expect(screen.getByText(/john doe/i)).toBeInTheDocument();
        });
      });
      
  3. 状态管理测试

    • Redux测试
      import configureStore from 'redux-mock-store';
      import { Provider } from 'react-redux';
      import { render } from '@testing-library/react';
      import Component from './Component';
      
      const mockStore = configureStore([]);
      
      test('renders with initial state', () => {
        const store = mockStore({ user: { name: 'John' } });
        
        render(
          <Provider store={store}>
            <Component />
          </Provider>
        );
      
        expect(screen.getByText(/john/i)).toBeInTheDocument();
      });
      
E2E测试
  1. Cypress配置

    • 基本配置
      // cypress.config.js
      module.exports = {
        e2e: {
          baseUrl: 'http://localhost:3000',
          supportFile: false,
          viewportWidth: 1280,
          viewportHeight: 800
        }
      };
      
    • 环境变量
      {
        "env": {
          "API_URL": "http://localhost:4000",
          "AUTH_TOKEN": "test-token"
        }
      }
      
  2. 页面导航测试

    • 路由测试
      describe('Navigation', () => {
        it('should navigate to about page', () => {
          cy.visit('/');
          cy.get('a[href*="about"]').click();
          cy.url().should('include', '/about');
          cy.get('h1').contains('About Page');
        });
      });
      
  3. 表单提交测试

    • 完整流程测试
      describe('Login Form', () => {
        it('should submit form and redirect to dashboard', () => {
          cy.visit('/login');
          cy.get('#username').type('testuser');
          cy.get('#password').type('password123');
          cy.get('form').submit();
          cy.url().should('include', '/dashboard');
          cy.get('.welcome-message').should('contain', 'Welcome testuser');
        });
      });
      
  4. API交互测试

    • 网络请求测试
      describe('API Interaction', () => {
        it('should fetch data and display results', () => {
          cy.intercept('GET', '/api/data', { fixture: 'data.json' }).as('getData');
          
          cy.visit('/data');
          cy.wait('@getData');
          
          cy.get('.data-item').should('have.length', 5);
        });
      });
      
测试覆盖率
  1. Jest配置

    • 覆盖率配置
      {
        "collectCoverage": true,
        "coverageReporters": ["text", "lcov"],
        "coverageThreshold": {
          "global": {
            "branches": 80,
            "functions": 80,
            "lines": 80,
            "statements": 80
          }
        }
      }
      
    • 忽略文件
      {
        "coveragePathIgnorePatterns": [
          "/node_modules/",
          "/tests/",
          "/mocks/"
        ]
      }
      
  2. 生成报告

    • 命令行生成
      npm test -- --coverage
      
    • CI集成
      - name: Run tests
        run: npm test -- --coverage
      - name: Upload coverage
        uses: codecov/codecov-action@v3
      
  3. 报告分析

    • 查看HTML报告
      open coverage/lcov-report/index.html
      
    • 代码质量指标
      • 分支覆盖率
      • 函数覆盖率
      • 行覆盖率
      • 语句覆盖率
测试最佳实践
  1. 测试命名规范

    • 描述性命名
      test('should display error message when form is submitted empty')
      
    • Given-When-Then模式
      describe('when form is submitted', () => {
        it('should display error message', () => {
          // Given
          render(<Form />);
          
          // When
          fireEvent.click(screen.getByRole('button'));
          
          // Then
          expect(screen.getByText(/required/i)).toBeInTheDocument();
        });
      });
      
  2. 测试数据管理

    • 使用工厂函数
      const createUser = (overrides = {}) => ({
        id: 1,
        name: 'John Doe',
        email: 'john@example.com',
        ...overrides
      });
      
    • 使用faker.js
      import { faker } from '@faker-js/faker';
      
      const mockUser = {
        id: faker.datatype.number(),
        name: faker.name.fullName(),
        email: faker.internet.email()
      };
      
  3. 测试隔离

    • 独立测试环境
      beforeEach(() => {
        jest.resetAllMocks();
        cleanup();
      });
      
    • 避免测试间依赖
      test('should increment counter', () => {
        const { result } = renderHook(() => useCounter());
        act(() => result.current.increment());
        expect(result.current.count).toBe(1);
      });
      
      test('should decrement counter', () => {
        const { result } = renderHook(() => useCounter());
        act(() => result.current.decrement());
        expect(result.current.count).toBe(-1);
      });
      
  4. 测试性能优化

    • 并行执行
      {
        "jest": {
          "maxWorkers": "50%"
        }
      }
      
    • 测试缓存
      {
        "cache": true,
        "cacheDirectory": "/tmp/jest_cache"
      }
      
测试工具集成
  1. 持续集成

    • GitHub Actions配置
      name: CI
      on: [push, pull_request]
      jobs:
        test:
          runs-on: ubuntu-latest
          steps:
            - uses: actions/checkout@v3
            - uses: actions/setup-node@v3
              with:
                node-version: 16
            - run: npm install
            - run: npm test
      
  2. 代码质量检查

    • ESLint集成
      {
        "scripts": {
          "lint": "eslint 'src/**/*.{js,jsx}'"
        }
      }
      
    • Prettier集成
      {
        "scripts": {
          "format": "prettier --write 'src/**/*.{js,jsx}'"
        }
      }
      
  3. 测试报告

    • JUnit报告
      {
        "reporters": [
          "default",
          ["jest-junit", { outputDirectory: "test-results" }]
        ]
      }
      
    • HTML报告
      {
        "reporters": [
          "default",
          ["jest-html-reporter", { outputPath: "test-report.html" }]
        ]
      }
      
测试策略优化
  1. 测试优先级

    • 核心功能优先
    • 高风险模块优先
    • 频繁变更模块优先
  2. 测试自动化

    • 持续集成
    • 自动部署
    • 监控告警
  3. 测试维护

    • 定期重构测试
    • 更新测试数据
    • 优化测试性能
测试驱动开发(TDD)
  1. 开发流程

    • 编写失败测试
    • 实现最小功能
    • 重构代码
  2. 示例

    // 1. 编写测试
    test('should add two numbers', () => {
      expect(add(1, 2)).toBe(3);
    });
    
    // 2. 实现功能
    function add(a, b) {
      return a + b;
    }
    
    // 3. 重构优化
    function add(...numbers) {
      return numbers.reduce((sum, num) => sum + num, 0);
    }
    
  3. 优势

    • 提高代码质量
    • 减少回归问题
    • 促进模块化设计
行为驱动开发(BDD)
  1. 开发流程

    • 定义用户故事
    • 编写场景测试
    • 实现功能
  2. 示例

    Feature: Login
      Scenario: Successful login
        Given I am on the login page
        When I enter valid credentials
        Then I should be redirected to dashboard
    
  3. 工具支持

    • Cucumber.js
    • Jest-cucumber
    • Cypress-cucumber-preprocessor
测试监控
  1. 测试结果监控

    • 失败率
    • 执行时间
    • 覆盖率趋势
  2. 告警机制

    • 测试失败告警
    • 覆盖率下降告警
    • 性能下降告警
  3. 可视化报告

    • 测试趋势图
    • 覆盖率图表
    • 性能指标
测试环境管理
  1. 环境隔离

    • 开发环境
    • 测试环境
    • 生产环境
  2. 环境配置

    • 环境变量
    • 配置文件
    • 数据库隔离
  3. 环境切换

    • 自动化部署
    • 环境验证
    • 回滚机制
测试数据管理
  1. 数据隔离
    • 独立数据库
    • 数据清理

React 完全指南:从入门到精通

1. React 简介

React 是一个用于构建用户界面的 JavaScript 库,由 Facebook 开发并维护。它具有以下特点:

  • 组件化:将 UI 拆分为独立可复用的组件
  • 声明式:通过 JSX 描述 UI 状态
  • 高效:虚拟 DOM 实现高性能渲染
  • 单向数据流:数据自上而下流动

2. 环境搭建

2.1 使用 Create React App

npx create-react-app my-app
cd my-app
npm start

2.2 项目结构

my-app/
  ├── node_modules/
  ├── public/
  │   └── index.html
  ├── src/
  │   ├── App.css
  │   ├── App.js
  │   ├── index.css
  │   └── index.js
  ├── package.json
  └── README.md

3. 核心概念

3.1 JSX 语法

const element = <h1>Hello, world!</h1>;

3.2 组件

函数组件
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}
类组件
class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

3.3 Props 和 State

  • Props:父组件传递给子组件的数据
  • State:组件内部维护的状态
class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

3.4 事件处理

function ActionButton() {
  function handleClick() {
    console.log('Button clicked');
  }

  return (
    <button onClick={handleClick}>
      Click Me
    </button>
  );
}

4. 高级特性

4.1 Hooks

useState
import React, { useState } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}
useEffect
import React, { useState, useEffect } from 'react';

function Example() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

4.2 Context API

const ThemeContext = React.createContext('light');

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <Toolbar />
    </ThemeContext.Provider>
  );
}

function Toolbar() {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

function ThemedButton() {
  const theme = useContext(ThemeContext);
  return <button theme={theme}>I am styled by theme context!</button>;
}

4.3 React Router

import {
  BrowserRouter as Router,
  Switch,
  Route,
  Link
} from "react-router-dom";

function App() {
  return (
    <Router>
      <nav>
        <Link to="/">Home</Link>
        <Link to="/about">About</Link>
      </nav>

      <Switch>
        <Route path="/about">
          <About />
        </Route>
        <Route path="/">
          <Home />
        </Route>
      </Switch>
    </Router>
  );
}

5. 最佳实践

5.1 组件设计原则

  • 单一职责原则
  • 可复用性
  • 可组合性
  • 可测试性

5.2 性能优化

5.2.1 组件优化
  1. React.memo

    const MyComponent = React.memo(function MyComponent(props) {
      // 组件实现
    });
    
  2. PureComponent

    class MyComponent extends React.PureComponent {
      render() {
        return <div>{this.props.value}</div>;
      }
    }
    
  3. shouldComponentUpdate

    class MyComponent extends React.Component {
      shouldComponentUpdate(nextProps, nextState) {
        return nextProps.value !== this.props.value;
      }
      
      render() {
        return <div>{this.props.value}</div>;
      }
    }
    
5.2.2 Hooks优化
  1. useMemo

    const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
    
  2. useCallback

    const memoizedCallback = useCallback(() => {
      doSomething(a, b);
    }, [a, b]);
    
  3. useReducer

    const [state, dispatch] = useReducer(reducer, initialState);
    
5.2.3 代码分割
  1. React.lazy

    const OtherComponent = React.lazy(() => import('./OtherComponent'));
    
    function MyComponent() {
      return (
        <React.Suspense fallback={<div>Loading...</div>}>
          <OtherComponent />
        </React.Suspense>
      );
    }
    
  2. 路由懒加载

    const Home = React.lazy(() => import('./routes/Home'));
    const About = React.lazy(() => import('./routes/About'));
    
    function App() {
      return (
        <Suspense fallback={<div>Loading...</div>}>
          <Routes>
            <Route path="/" element={<Home />} />
            <Route path="/about" element={<About />} />
          </Routes>
        </Suspense>
      );
    }
    
5.2.4 列表优化
  1. 虚拟列表

    import { FixedSizeList as List } from 'react-window';
    
    const Row = ({ index, style }) => (
      <div style={style}>Row {index}</div>
    );
    
    const Example = () => (
      <List
        height={150}
        itemCount={1000}
        itemSize={35}
        width={300}
      >
        {Row}
      </List>
    );
    
  2. 窗口化渲染

    import { VariableSizeList as List } from 'react-window';
    
    const rowHeights = new Array(1000)
      .fill(true)
      .map(() => 25 + Math.round(Math.random() * 50));
    
    const getItemSize = index => rowHeights[index];
    
    const Row = ({ index, style }) => (
      <div style={style}>Row {index}</div>
    );
    
    const Example = () => (
      <List
        height={150}
        itemCount={1000}
        itemSize={getItemSize}
        width={300}
      >
        {Row}
      </List>
    );
    
5.2.5 渲染优化
  1. 批量更新

    import { unstable_batchedUpdates } from 'react-dom';
    
    function handleClick() {
      unstable_batchedUpdates(() => {
        setCount(c => c + 1);
        setFlag(f => !f);
      });
    }
    
  2. 避免不必要的渲染

    function ParentComponent() {
      const [count, setCount] = useState(0);
      
      return (
        <>
          <button onClick={() => setCount(c => c + 1)}>Increment</button>
          <MemoizedChildComponent />
        </>
      );
    }
    
  3. 使用Fragment

    function MyComponent() {
      return (
        <>
          <ChildA />
          <ChildB />
          <ChildC />
        </>
      );
    }
    
5.2.6 内存优化
  1. 清理副作用

    useEffect(() => {
      const subscription = props.source.subscribe();
      return () => {
        subscription.unsubscribe();
      };
    }, [props.source]);
    
  2. 避免内存泄漏

    useEffect(() => {
      let isMounted = true;
      
      async function fetchData() {
        const result = await someAsyncOperation();
        if (isMounted) {
          setData(result);
        }
      }
    
      fetchData();
      
      return () => {
        isMounted = false;
      };
    }, []);
    
  3. 使用WeakMap

    const cache = new WeakMap();
    
    function getCachedData(obj) {
      if (!cache.has(obj)) {
        const result = computeExpensiveValue(obj);
        cache.set(obj, result);
      }
      return cache.get(obj);
    }
    
5.2.7 网络优化
  1. 预加载资源

    import { Prefetch } from 'react-static';
    
    function MyComponent() {
      return (
        <Prefetch path="/about">
          {({ loading, loaded }) => (
            <Link to="/about">
              About {loading ? 'Loading...' : 'Loaded'}
            </Link>
          )}
        </Prefetch>
      );
    }
    
  2. 数据预取

    const { data } = useSWR('/api/data', fetcher, {
      revalidateOnFocus: false,
      revalidateOnReconnect: false
    });
    
  3. 资源优先级

    <link rel="preload" href="critical.css" as="style">
    <link rel="preload" href="main.js" as="script">
    
5.2.8 工具使用
  1. React Profiler

    import { Profiler } from 'react';
    
    function onRenderCallback(
      id,
      phase,
      actualDuration,
      baseDuration,
      startTime,
      commitTime,
      interactions
    ) {
      console.log('Render time:', actualDuration);
    }
    
    function App() {
      return (
        <Profiler id="App" onRender={onRenderCallback}>
          <MyComponent />
        </Profiler>
      );
    }
    
  2. React DevTools

    • 组件性能分析
    • 状态调试
    • 组件树检查
  3. Lighthouse

    • 性能评分
    • 最佳实践检查
    • 可访问性评估

5.3 测试策略

5.3.1 单元测试
  1. 组件测试

    import { render, screen } from '@testing-library/react';
    import Button from './Button';
    
    test('renders button with correct text', () => {
      render(<Button>Click me</Button>);
      const buttonElement = screen.getByText(/click me/i);
      expect(buttonElement).toBeInTheDocument();
    });
    
  2. Hooks测试

    import { renderHook, act } from '@testing-library/react-hooks';
    import useCounter from './useCounter';
    
    test('should increment counter', () => {
      const { result } = renderHook(() => useCounter());
    
      act(() => {
        result.current.increment();
      });
    
      expect(result.current.count).toBe(1);
    });
    
  3. 快照测试

    import renderer from 'react-test-renderer';
    import Component from './Component';
    
    test('matches snapshot', () => {
      const tree = renderer.create(<Component />).toJSON();
      expect(tree).toMatchSnapshot();
    });
    
5.3.2 集成测试
  1. 组件交互测试

    import { render, screen, fireEvent } from '@testing-library/react';
    import LoginForm from './LoginForm';
    
    test('submits form with correct values', () => {
      const handleSubmit = jest.fn();
      render(<LoginForm onSubmit={handleSubmit} />);
    
      fireEvent.change(screen.getByLabelText(/username/i), {
        target: { value: 'testuser' }
      });
    
      fireEvent.change(screen.getByLabelText(/password/i), {
        target: { value: 'password123' }
      });
    
      fireEvent.click(screen.getByRole('button', { name: /submit/i }));
    
      expect(handleSubmit).toHaveBeenCalledWith({
        username: 'testuser',
        password: 'password123'
      });
    });
    
  2. API集成测试

    import { render, screen, waitFor } from '@testing-library/react';
    import axios from 'axios';
    import UserList from './UserList';
    
    jest.mock('axios');
    
    test('fetches and displays users', async () => {
      axios.get.mockResolvedValue({
        data: [{ id: 1, name: 'John Doe' }]
      });
    
      render(<UserList />);
    
      await waitFor(() => {
        expect(screen.getByText(/john doe/i)).toBeInTheDocument();
      });
    });
    
5.3.3 E2E测试
  1. Cypress配置

    // cypress.config.js
    module.exports = {
      e2e: {
        baseUrl: 'http://localhost:3000',
        supportFile: false
      }
    };
    
  2. 页面导航测试

    describe('Navigation', () => {
      it('should navigate to about page', () => {
        cy.visit('/');
        cy.get('a[href*="about"]').click();
        cy.url().should('include', '/about');
        cy.get('h1').contains('About Page');
      });
    });
    
  3. 表单提交测试

    describe('Login Form', () => {
      it('should submit form', () => {
        cy.visit('/login');
        cy.get('#username').type('testuser');
        cy.get('#password').type('password123');
        cy.get('form').submit();
        cy.url().should('include', '/dashboard');
      });
    });
    
5.3.4 测试覆盖率
  1. Jest配置

    {
      "collectCoverage": true,
      "coverageReporters": ["text", "lcov"],
      "coverageThreshold": {
        "global": {
          "branches": 80,
          "functions": 80,
          "lines": 80,
          "statements": 80
        }
      }
    }
    
  2. 生成覆盖率报告

    npm test -- --coverage
    
  3. 查看HTML报告

    open coverage/lcov-report/index.html
    
5.3.5 测试最佳实践
  1. 测试金字塔

    • 70% 单元测试
    • 20% 集成测试
    • 10% E2E测试
  2. 测试命名规范

    • 描述性测试名称
    • Given-When-Then模式
    • 避免使用"should"
  3. 测试数据管理

    • 使用工厂函数生成测试数据
    • 避免硬编码
    • 使用faker.js生成随机数据
  4. 测试隔离

    • 每个测试独立运行
    • 使用beforeEach/afterEach清理状态
    • 避免测试间依赖

6. 生态系统

6.1 状态管理

Redux
  1. 核心概念

    • Store:应用状态容器
    • Action:状态变更描述
    • Reducer:状态变更处理
    • Middleware:扩展功能
  2. 示例代码

    // store.js
    import { createStore } from 'redux';
    
    function counterReducer(state = { value: 0 }, action) {
      switch (action.type) {
        case 'increment':
          return { value: state.value + 1 };
        default:
          return state;
      }
    }
    
    const store = createStore(counterReducer);
    
    // Component.js
    import { useSelector, useDispatch } from 'react-redux';
    
    function Counter() {
      const count = useSelector(state => state.value);
      const dispatch = useDispatch();
    
      return (
        <div>
          <span>{count}</span>
          <button onClick={() => dispatch({ type: 'increment' })}>
            Increment
          </button>
        </div>
      );
    }
    
Recoil
  1. 核心概念

    • Atom:状态单元
    • Selector:派生状态
    • Hooks:状态访问
  2. 示例代码

    // state.js
    import { atom } from 'recoil';
    
    export const countState = atom({
      key: 'countState',
      default: 0,
    });
    
    // Component.js
    import { useRecoilState } from 'recoil';
    import { countState } from './state';
    
    function Counter() {
      const [count, setCount] = useRecoilState(countState);
    
      return (
        <div>
          <span>{count}</span>
          <button onClick={() => setCount(count + 1)}>
            Increment
          </button>
        </div>
      );
    }
    

6.2 样式方案

Styled Components
  1. 基本用法

    import styled from 'styled-components';
    
    const Button = styled.button`
      background: ${props => props.primary ? 'palevioletred' : 'white'};
      color: ${props => props.primary ? 'white' : 'palevioletred'};
      font-size: 1em;
      padding: 0.25em 1em;
      border: 2px solid palevioletred;
      border-radius: 3px;
    `;
    
    function Example() {
      return (
        <div>
          <Button>Normal</Button>
          <Button primary>Primary</Button>
        </div>
      );
    }
    
  2. 主题支持

    import { ThemeProvider } from 'styled-components';
    
    const theme = {
      colors: {
        primary: 'palevioletred',
        secondary: 'white'
      }
    };
    
    function App() {
      return (
        <ThemeProvider theme={theme}>
          <Button>Styled</Button>
        </ThemeProvider>
      );
    }
    
CSS Modules
  1. 基本用法

    /* Button.module.css */
    .button {
      background-color: white;
      color: palevioletred;
      font-size: 1em;
      padding: 0.25em 1em;
      border: 2px solid palevioletred;
      border-radius: 3px;
    }
    
    import styles from './Button.module.css';
    
    function Button() {
      return (
        <button className={styles.button}>
          Click me
        </button>
      );
    }
    
  2. 组合样式

    .primary {
      background-color: palevioletred;
      color: white;
    }
    
    function Button({ primary }) {
      return (
        <button className={`${styles.button} ${primary && styles.primary}`}>
          Click me
        </button>
      );
    }
    

6.3 表单处理

Formik
  1. 基本用法

    import { Formik, Form, Field } from 'formik';
    
    function LoginForm() {
      return (
        <Formik
          initialValues={{ email: '', password: '' }}
          onSubmit={values => {
            console.log(values);
          }}
        >
          <Form>
            <Field name="email" type="email" />
            <Field name="password" type="password" />
            <button type="submit">Submit</button>
          </Form>
        </Formik>
      );
    }
    
  2. 表单验证

    function validate(values) {
      const errors = {};
      if (!values.email) {
        errors.email = 'Required';
      } else if (
        !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email)
      ) {
        errors.email = 'Invalid email address';
      }
      return errors;
    }
    
React Hook Form
  1. 基本用法

    import { useForm } from 'react-hook-form';
    
    function LoginForm() {
      const { register, handleSubmit } = useForm();
      const onSubmit = data => console.log(data);
    
      return (
        <form onSubmit={handleSubmit(onSubmit)}>
          <input {...register('email')} />
          <input {...register('password')} type="password" />
          <button type="submit">Submit</button>
        </form>
      );
    }
    
  2. 表单验证

    <input
      {...register('email', {
        required: 'Email is required',
        pattern: {
          value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
          message: 'Invalid email address'
        }
      })}
    />
    

6.4 数据获取

React Query
  1. 基本用法

    import { useQuery } from 'react-query';
    
    function UserList() {
      const { data, isLoading, error } = useQuery('users', () =>
        fetch('/api/users').then(res => res.json())
      );
    
      if (isLoading) return 'Loading...';
      if (error) return 'Error!';
    
      return (
        <ul>
          {data.map(user => (
            <li key={user.id}>{user.name}</li>
          ))}
        </ul>
      );
    }
    
  2. 数据缓存

    const { data } = useQuery('users', fetchUsers, {
      staleTime: 1000 * 60 * 5, // 5 minutes
      cacheTime: 1000 * 60 * 10 // 10 minutes
    });
    
SWR
  1. 基本用法

    import useSWR from 'swr';
    
    function Profile() {
      const { data, error } = useSWR('/api/user', fetcher);
    
      if (error) return <div>failed to load</div>;
      if (!data) return <div>loading...</div>;
      return <div>hello {data.name}!</div>;
    }
    
  2. 自动重试

    const { data } = useSWR('/api/user', fetcher, {
      onErrorRetry: (error, key, config, revalidate, { retryCount }) => {
        if (error.status === 404) return;
        if (retryCount >= 10) return;
        setTimeout(() => revalidate({ retryCount }), 5000);
      }
    });
    

6.5 UI 库

Material-UI
  1. 基本用法

    import { Button, TextField } from '@mui/material';
    
    function Form() {
      return (
        <form>
          <TextField label="Email" variant="outlined" />
          <TextField label="Password" type="password" variant="outlined" />
          <Button variant="contained" color="primary">
            Submit
          </Button>
        </form>
      );
    }
    
  2. 主题定制

    import { createTheme, ThemeProvider } from '@mui/material/styles';
    
    const theme = createTheme({
      palette: {
        primary: {
          main: '#1976d2',
        },
      },
    });
    
    function App() {
      return (
        <ThemeProvider theme={theme}>
          <Form />
        </ThemeProvider>
      );
    }
    
Ant Design
  1. 基本用法

    import { Button, Input } from 'antd';
    
    function Form() {
      return (
        <form>
          <Input placeholder="Email" />
          <Input.Password placeholder="Password" />
          <Button type="primary">Submit</Button>
        </form>
      );
    }
    
  2. 主题定制

    import { ConfigProvider } from 'antd';
    
    function App() {
      return (
        <ConfigProvider
          theme={{
            token: {
              colorPrimary: '#00b96b',
            },
          }}
        >
          <Form />
        </ConfigProvider>
      );
    }
    

7. 学习资源

  • 官方文档:https://reactjs.org/
  • React 中文文档:https://zh-hans.reactjs.org/
  • React 入门教程:https://react.iamkasong.com/
  • React 进阶指南:https://react.iamkasong.com/advanced/
  • React 源码解析:https://react.iamkasong.com/implementation/

8. React 18 新特性

8.1 并发渲染(Concurrent Rendering)

  • 可中断渲染:允许React在渲染过程中暂停和恢复
  • 自动批处理:自动合并多个状态更新
  • 过渡更新:区分紧急和非紧急更新
import { startTransition } from 'react';

// 紧急更新
setInputValue(input);

// 非紧急更新
startTransition(() => {
  setSearchQuery(input);
});

8.2 新的Hooks

  • useId:生成唯一ID
  • useSyncExternalStore:与外部存储同步
  • useInsertionEffect:用于CSS-in-JS库

8.3 服务端渲染改进

  • 流式SSR:支持渐进式HTML流
  • 选择性水合:优先水合重要部分

8.4 其他改进

  • 新的根API:createRoot
  • 严格模式增强:检测不安全的副作用
  • 改进的错误处理:自动恢复错误边界

9. Server Components

9.1 概念与优势

  • 在服务端渲染组件
  • 减少客户端包体积
  • 直接访问后端服务
  • 自动代码分割

9.2 使用场景

  • 数据获取密集型组件
  • 静态内容
  • 大型依赖组件
  • 安全性要求高的组件

9.3 示例代码

// Note.server.js
import db from 'db.server';

function Note({id}) {
  const note = db.notes.get(id);
  return <NoteView note={note} />;
}

// NoteView.client.js
'use client';

function NoteView({note}) {
  return (
    <div>
      <h1>{note.title}</h1>
      <p>{note.content}</p>
    </div>
  );
}

9.4 注意事项

  • 客户端交互限制
  • 状态管理方式
  • 网络延迟影响
  • 开发环境配置

10. 状态管理库对比

10.1 Redux

  • 优点:
    • 成熟的生态系统
    • 强大的中间件支持
    • 时间旅行调试
    • 严格的单向数据流
  • 缺点:
    • 样板代码较多
    • 学习曲线较陡
    • 可能过度设计简单场景

10.2 Recoil

  • 优点:
    • 原生支持React
    • 细粒度状态管理
    • 异步数据支持
    • 更简单的API
  • 缺点:
    • 相对较新
    • 生态系统不够成熟
    • 调试工具有限

10.3 Zustand

  • 优点:
    • 极简API
    • 高性能
    • 支持中间件
    • 易于集成
  • 缺点:
    • 功能相对基础
    • 社区支持较少
    • 缺少内置异步处理

10.4 选择建议

场景推荐方案
大型复杂应用Redux
中小型应用Recoil
简单应用Zustand
需要时间旅行调试Redux
需要细粒度更新Recoil
追求极简实现Zustand

11. 常见问题与解决方案

11.1 性能问题

  • 问题:组件频繁重新渲染
    • 解决方案:使用React.memo、useMemo、useCallback优化
  • 问题:长列表渲染卡顿
    • 解决方案:使用虚拟列表库(如react-window)

11.2 状态管理

  • 问题:组件间状态共享困难
    • 解决方案:使用Context API或状态管理库
  • 问题:状态更新导致意外渲染
    • 解决方案:检查依赖数组,使用useEffect正确管理副作用

11.3 路由问题

  • 问题:页面刷新后404
    • 解决方案:配置服务器支持HTML5 History API
  • 问题:路由切换时数据丢失
    • 解决方案:使用状态管理或路由缓存

11.4 样式冲突

  • 问题:全局样式污染
    • 解决方案:使用CSS Modules或CSS-in-JS
  • 问题:第三方组件样式覆盖
    • 解决方案:使用CSS命名空间或样式优先级调整

11.5 开发环境

  • 问题:热更新失效
    • 解决方案:检查webpack配置,确保HMR启用
  • 问题:TypeScript类型错误
    • 解决方案:安装正确的类型定义文件(@types包)

12. 项目架构设计

12.1 分层架构

展示层(Presentation Layer)
  1. 职责

    • UI展示
    • 用户交互
    • 数据展示
    • 样式管理
  2. 技术栈

    • React组件
    • CSS-in-JS
    • UI库(如Material-UI)
    • 动画库(如Framer Motion)
  3. 示例代码

    // components/Button.jsx
    import styled from 'styled-components';
    
    const StyledButton = styled.button`
      background: ${props => props.primary ? 'blue' : 'white'};
      color: ${props => props.primary ? 'white' : 'blue'};
      padding: 0.5rem 1rem;
      border-radius: 4px;
      cursor: pointer;
    `;
    
    function Button({ primary, children }) {
      return <StyledButton primary={primary}>{children}</StyledButton>;
    }
    
业务逻辑层(Business Logic Layer)
  1. 职责

    • 业务规则处理
    • 数据转换
    • 状态管理
    • 业务逻辑复用
  2. 技术栈

    • 自定义Hooks
    • 服务类
    • 状态管理库(如Redux、Recoil)
    • 数据验证库(如Yup)
  3. 示例代码

    // hooks/useForm.js
    import { useState } from 'react';
    
    export function useForm(initialValues) {
      const [values, setValues] = useState(initialValues);
    
      const handleChange = (e) => {
        const { name, value } = e.target;
        setValues({
          ...values,
          [name]: value
        });
      };
    
      return {
        values,
        handleChange
      };
    }
    
数据访问层(Data Access Layer)
  1. 职责

    • API调用
    • 数据缓存
    • 错误处理
    • 数据转换
  2. 技术栈

    • Axios
    • React Query
    • GraphQL客户端(如Apollo)
    • WebSocket
  3. 示例代码

    // services/api.js
    import axios from 'axios';
    
    const api = axios.create({
      baseURL: process.env.REACT_APP_API_URL,
      timeout: 10000,
      headers: {
        'Content-Type': 'application/json'
      }
    });
    
    export const getUsers = async () => {
      try {
        const response = await api.get('/users');
        return response.data;
      } catch (error) {
        throw new Error(error.response?.data?.message || 'Failed to fetch users');
      }
    };
    

12.2 目录结构

基础结构
src/
├── assets/          # 静态资源
│   ├── images/      # 图片资源
│   ├── fonts/       # 字体文件
│   └── styles/      # 全局样式
├── components/      # 通用组件
│   ├── ui/          # UI组件
│   ├── layout/      # 布局组件
│   └── shared/      # 共享组件
├── features/        # 功能模块
│   └── featureName/
│       ├── api/     # API接口
│       ├── hooks/   # 自定义Hooks
│       ├── store/   # 状态管理
│       ├── types/   # 类型定义
│       └── views/   # 页面组件
├── lib/             # 工具函数
│   ├── utils/       # 通用工具
│   ├── constants/   # 常量定义
│   └── helpers/     # 辅助函数
├── pages/           # 页面路由
│   ├── Home/        # 首页
│   ├── About/       # 关于页
│   └── NotFound/    # 404页
├── services/        # API服务
│   ├── api.js       # API配置
│   ├── auth.js      # 认证服务
│   └── storage.js   # 存储服务
├── store/           # 全局状态
│   ├── slices/      # Redux切片
│   ├── actions/     # Redux动作
│   └── reducers/    # Redux reducer
├── styles/          # 全局样式
│   ├── theme.js     # 主题配置
│   ├── global.css   # 全局样式
│   └── variables.css # CSS变量
└── utils/           # 工具函数
    ├── validators/  # 验证工具
    ├── formatters/  # 格式化工具
    └── hooks/       # 自定义Hooks
模块化结构
src/
├── modules/
│   ├── auth/        # 认证模块
│   │   ├── api/
│   │   ├── components/
│   │   ├── hooks/
│   │   ├── store/
│   │   └── views/
│   ├── user/        # 用户模块
│   │   ├── api/
│   │   ├── components/
│   │   ├── hooks/
│   │   ├── store/
│   │   └── views/
│   └── product/     # 产品模块
│       ├── api/
│       ├── components/
│       ├── hooks/
│       ├── store/
│       └── views/

12.3 模块化设计

模块划分原则
  1. 按业务功能划分

    • 用户管理
      • 用户注册/登录
      • 用户信息管理
      • 权限控制
    • 订单管理
      • 订单创建
      • 订单查询
      • 订单状态管理
    • 产品管理
      • 产品展示
      • 产品分类
      • 产品搜索
    • 权限管理
      • 角色管理
      • 权限分配
      • 访问控制
  2. 按技术特性划分

    • 认证模块
      • 登录/注册
      • 会话管理
      • 权限验证
    • 支付模块
      • 支付接口
      • 支付状态管理
      • 支付回调处理
    • 通知模块
      • 消息推送
      • 邮件通知
      • 站内信
    • 日志模块
      • 操作日志
      • 错误日志
      • 访问日志
  3. 模块间通信

    • 通过props传递数据
      // ParentComponent.jsx
      function ParentComponent() {
        const [data, setData] = useState(null);
        return <ChildComponent data={data} onDataChange={setData} />;
      }
      
      // ChildComponent.jsx
      function ChildComponent({ data, onDataChange }) {
        return (
          <input 
            value={data || ''}
            onChange={(e) => onDataChange(e.target.value)}
          />
        );
      }
      
    • 使用Context API共享状态
      // ThemeContext.js
      const ThemeContext = createContext();
      
      function ThemeProvider({ children }) {
        const [theme, setTheme] = useState('light');
        return (
          <ThemeContext.Provider value={{ theme, setTheme }}>
            {children}
          </ThemeContext.Provider>
        );
      }
      
      // ThemedButton.jsx
      function ThemedButton() {
        const { theme, setTheme } = useContext(ThemeContext);
        return (
          <button 
            style={{ backgroundColor: theme === 'light' ? '#fff' : '#333' }}
            onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}
          >
            Toggle Theme
          </button>
        );
      }
      
    • 通过事件总线通信
      // eventBus.js
      class EventBus {
        constructor() {
          this.events = {};
        }
      
        on(event, callback) {
          if (!this.events[event]) {
            this.events[event] = [];
          }
          this.events[event].push(callback);
        }
      
        emit(event, ...args) {
          if (this.events[event]) {
            this.events[event].forEach(callback => callback(...args));
          }
        }
      }
      
      export default new EventBus();
      
    • 使用状态管理库
      // store.js
      import { configureStore } from '@reduxjs/toolkit';
      import authReducer from './authSlice';
      import userReducer from './userSlice';
      
      export default configureStore({
        reducer: {
          auth: authReducer,
          user: userReducer
        }
      });
      
模块接口设计
  1. 组件接口

    // modules/auth/components/LoginForm.jsx
    import PropTypes from 'prop-types';
    
    function LoginForm({ onSubmit, loading, error }) {
      return (
        <form onSubmit={onSubmit}>
          {error && <div className="error">{error}</div>}
          <input name="email" type="email" required />
          <input name="password" type="password" required />
          <button type="submit" disabled={loading}>
            {loading ? 'Loading...' : 'Login'}
          </button>
        </form>
      );
    }
    
    LoginForm.propTypes = {
      onSubmit: PropTypes.func.isRequired,
      loading: PropTypes.bool,
      error: PropTypes.string
    };
    
    LoginForm.defaultProps = {
      loading: false,
      error: null
    };
    
  2. API接口

    // modules/auth/api/auth.js
    import axios from 'axios';
    
    const API_BASE_URL = process.env.REACT_APP_API_URL;
    
    export const login = async (credentials) => {
      try {
        const response = await axios.post(`${API_BASE_URL}/auth/login`, credentials);
        return response.data;
      } catch (error) {
        throw new Error(error.response?.data?.message || 'Login failed');
      }
    };
    
    export const register = async (userData) => {
      try {
        const response = await axios.post(`${API_BASE_URL}/auth/register`, userData);
        return response.data;
      } catch (error) {
        throw new Error(error.response?.data?.message || 'Registration failed');
      }
    };
    
    export const logout = async () => {
      try {
        await axios.post(`${API_BASE_URL}/auth/logout`);
      } catch (error) {
        throw new Error(error.response?.data?.message || 'Logout failed');
      }
    };
    
  3. 状态接口

    // modules/auth/store/authSlice.js
    import { createSlice } from '@reduxjs/toolkit';
    
    const initialState = {
      user: null,
      loading: false,
      error: null
    };
    
    const authSlice = createSlice({
      name: 'auth',
      initialState,
      reducers: {
        loginStart(state) {
          state.loading = true;
          state.error = null;
        },
        loginSuccess(state, action) {
          state.user = action.payload;
          state.loading = false;
        },
        loginFailure(state, action) {
          state.error = action.payload;
          state.loading = false;
        },
        logoutSuccess(state) {
          state.user = null;
        }
      }
    });
    
    export const { loginStart, loginSuccess, loginFailure, logoutSuccess } = authSlice.actions;
    
    export const login = (credentials) => async (dispatch) => {
      try {
        dispatch(loginStart());
        const user = await loginAPI(credentials);
        dispatch(loginSuccess(user));
      } catch (error) {
        dispatch(loginFailure(error.message));
      }
    };
    
    export const logout = () => async (dispatch) => {
      try {
        await logoutAPI();
        dispatch(logoutSuccess());
      } catch (error) {
        console.error('Logout error:', error);
      }
    };
    
    export default authSlice.reducer;
    
  4. 类型接口(TypeScript)

    // modules/auth/types.ts
    export interface User {
      id: string;
      name: string;
      email: string;
      role: string;
    }
    
    export interface AuthState {
      user: User | null;
      loading: boolean;
      error: string | null;
    }
    
    export interface LoginCredentials {
      email: string;
      password: string;
    }
    
    export interface RegisterData extends LoginCredentials {
      name: string;
    }
    
模块依赖管理
  1. 模块间依赖

    • 使用依赖注入
      // modules/auth/authService.js
      class AuthService {
        constructor(apiClient) {
          this.apiClient = apiClient;
        }
      
        async login(credentials) {
          return this.apiClient.post('/auth/login', credentials);
        }
      }
      
      // main.js
      const apiClient = new ApiClient();
      const authService = new AuthService(apiClient);
      
  2. 模块版本控制

    • 使用语义化版本
      {
        "dependencies": {
          "@modules/auth": "^1.2.0",
          "@modules/user": "~2.0.1"
        }
      }
      
  3. 模块更新策略

    • 向后兼容
    • 版本迁移指南
    • 废弃警告
模块测试策略
  1. 单元测试

    // modules/auth/authSlice.test.js
    import authReducer, { loginStart, loginSuccess, loginFailure } from './authSlice';
    
    describe('auth reducer', () => {
      it('should handle initial state', () => {
        expect(authReducer(undefined, {})).toEqual({
          user: null,
          loading: false,
          error: null
        });
      });
    
      it('should handle loginStart', () => {
        expect(
          authReducer(undefined, loginStart())
        ).toEqual({
          user: null,
          loading: true,
          error: null
        });
      });
    });
    
  2. 集成测试

    // modules/auth/authService.test.js
    import AuthService from './authService';
    import MockAdapter from 'axios-mock-adapter';
    import axios from 'axios';
    
    describe('AuthService', () => {
      let mockAxios;
      let authService;
    
      beforeEach(() => {
        mockAxios = new MockAdapter(axios);
        authService = new AuthService(axios);
      });
    
      it('should login successfully', async () => {
        const credentials = { email: 'test@example.com', password: 'password' };
        const responseData = { token: 'abc123' };
    
        mockAxios.onPost('/auth/login').reply(200, responseData);
    
        const result = await authService.login(credentials);
        expect(result.data).toEqual(responseData);
      });
    });
    
  3. E2E测试

    // cypress/integration/auth.spec.js
    describe('Authentication', () => {
      it('should login successfully', () => {
        cy.visit('/login');
        cy.get('[name="email"]').type('test@example.com');
        cy.get('[name="password"]').type('password');
        cy.get('form').submit();
        cy.url().should('include', '/dashboard');
      });
    });
    
模块文档规范
  1. README模板

    # Auth Module
    
    ## 功能描述
    - 用户登录/注册
    - 会话管理
    - 权限验证
    
    ## 安装
    ```bash
    npm install @modules/auth
    

    使用示例

    import { login } from '@modules/auth';
    
    const credentials = {
      email: 'test@example.com',
      password: 'password123'
    };
    
    login(credentials)
      .then(user => console.log('Logged in:', user))
      .catch(error => console.error('Login failed:', error));
    

    API 文档

    • login(credentials)
    • register(userData)
    • logout()

    依赖

    • axios
    • redux
    
    
  2. API文档生成

    • 使用JSDoc
      /**
       * 用户登录
       * @param {Object} credentials - 登录凭证
       * @param {string} credentials.email - 用户邮箱
       * @param {string} credentials.password - 用户密码
       * @returns {Promise<User>} 返回用户信息
       * @throws {Error} 登录失败时抛出错误
       */
      export async function login(credentials) {
        // ...
      }
      
  3. 变更日志

    # Changelog
    
    ## [1.2.0] - 2023-03-15
    ### Added
    - 新增第三方登录支持
    
    ### Changed
    - 优化登录错误提示
    
    ### Fixed
    - 修复会话过期问题
    
模块发布流程
  1. 版本管理

    npm version patch
    npm version minor
    npm version major
    
  2. 发布到私有仓库

    npm publish --access restricted
    
  3. 自动更新

    {
      "scripts": {
        "postpublish": "git push && git push --tags"
      }
    }
    
模块最佳实践
  1. 单一职责原则

    • 每个模块只负责一个功能
    • 避免过度耦合
  2. 接口最小化

    • 只暴露必要的接口
    • 使用私有方法
  3. 依赖管理

    • 最小化外部依赖
    • 使用peerDependencies
  4. 性能优化

    • 懒加载模块
    • 代码分割
  5. 安全性

    • 输入验证
    • 权限控制
    • 错误处理

12.4 性能优化策略

代码分割
  1. 动态导入

    const LazyComponent = React.lazy(() => import('./LazyComponent'));
    
    function App() {
      return (
        <React.Suspense fallback={<div>Loading...</div>}>
          <LazyComponent />
        </React.Suspense>
      );
    }
    
  2. 路由懒加载

    const Home = React.lazy(() => import('./pages/Home'));
    const About = React.lazy(() => import('./pages/About'));
    
    function App() {
      return (
        <Suspense fallback={<div>Loading...</div>}>
          <Routes>
            <Route path="/" element={<Home />} />
            <Route path="/about" element={<About />} />
          </Routes>
        </Suspense>
      );
    }
    
  3. 组件级代码分割

    const HeavyComponent = React.lazy(() => 
      import('./HeavyComponent').then(module => ({
        default: module.HeavyComponent
      }))
    );
    
  4. 预加载策略

    const preloadComponent = (component) => {
      const Component = React.lazy(component);
      Component.preload();
    };
    
    // 鼠标悬停时预加载
    <div onMouseEnter={() => preloadComponent(() => import('./HeavyComponent'))}>
      Hover to preload
    </div>
    
资源优化
  1. 图片优化

    • 使用WebP格式
      <picture>
        <source srcSet="image.webp" type="image/webp" />
        <source srcSet="image.jpg" type="image/jpeg" /> 
        <img src="image.jpg" alt="Example" />
      </picture>
      
    • 响应式图片
      <img
        srcSet="image-320w.jpg 320w,
                image-480w.jpg 480w,
                image-800w.jpg 800w"
        sizes="(max-width: 320px) 280px,
               (max-width: 480px) 440px,
               800px"
        src="image-800w.jpg"
        alt="Example"
      />
      
    • 图片懒加载
      <img
        src="placeholder.jpg"
        data-src="real-image.jpg"
        className="lazyload"
        alt="Example"
      />
      
  2. 字体优化

    • 字体子集化
      @font-face {
        font-family: 'CustomFont';
        src: url('/fonts/custom-subset.woff2') format('woff2'),
             url('/fonts/custom-subset.woff') format('woff');
        unicode-range: U+000-5FF; /* 仅包含常用字符 */
      }
      
    • 字体预加载
      <link 
        rel="preload" 
        href="/fonts/custom.woff2" 
        as="font" 
        type="font/woff2" 
        crossorigin
      />
      
    • 字体显示策略
      @font-face {
        font-display: swap; /* 使用系统字体直到自定义字体加载完成 */
      }
      
  3. 资源压缩

    • Webpack配置示例
      module.exports = {
        optimization: {
          minimize: true,
          minimizer: [
            new TerserPlugin({
              parallel: true,
              terserOptions: {
                compress: true,
                mangle: true
              }
            }),
            new CssMinimizerPlugin()
          ]
        }
      };
      
缓存策略
  1. Service Worker

    • 缓存策略配置
      // service-worker.js
      const CACHE_NAME = 'v1';
      const ASSETS = [
        '/',
        '/index.html',
        '/main.js',
        '/style.css'
      ];
      
      self.addEventListener('install', (event) => {
        event.waitUntil(
          caches.open(CACHE_NAME)
            .then(cache => cache.addAll(ASSETS))
        );
      });
      
      self.addEventListener('fetch', (event) => {
        event.respondWith(
          caches.match(event.request)
            .then(response => response || fetch(event.request))
        );
      });
      
    • 缓存更新策略
      self.addEventListener('activate', (event) => {
        const cacheWhitelist = [CACHE_NAME];
        event.waitUntil(
          caches.keys().then(cacheNames => {
            return Promise.all(
              cacheNames.map(cacheName => {
                if (!cacheWhitelist.includes(cacheName)) {
                  return caches.delete(cacheName);
                }
              })
            );
          })
        );
      });
      
  2. HTTP缓存

    • Nginx配置
      location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
        add_header Vary "Accept-Encoding";
      }
      
    • ETag配置
      etag on;
      
  3. 数据缓存

    • React Query缓存配置
      const queryClient = new QueryClient({
        defaultOptions: {
          queries: {
            staleTime: 1000 * 60 * 5, // 5分钟
            cacheTime: 1000 * 60 * 10 // 10分钟
          }
        }
      });
      
渲染优化
  1. 虚拟列表

    • 固定高度列表
      import { FixedSizeList as List } from 'react-window';
      
      const Row = ({ index, style }) => (
        <div style={style}>Row {index}</div>
      );
      
      const Example = () => (
        <List
          height={150}
          itemCount={1000}
          itemSize={35}
          width={300}
        >
          {Row}
        </List>
      );
      
    • 可变高度列表
      import { VariableSizeList as List } from 'react-window';
      
      const rowHeights = new Array(1000)
        .fill(true)
        .map(() => 25 + Math.round(Math.random() * 50));
      
      const getItemSize = index => rowHeights[index];
      
      const Row = ({ index, style }) => (
        <div style={style}>Row {index}</div>
      );
      
      const Example = () => (
        <List
          height={150}
          itemCount={1000}
          itemSize={getItemSize}
          width={300}
        >
          {Row}
        </List>
      );
      
  2. 批量更新

    • React 18自动批处理
      function handleClick() {
        setCount(c => c + 1);
        setFlag(f => !f); // 自动批处理
      }
      
    • 手动批处理
      import { unstable_batchedUpdates } from 'react-dom';
      
      function handleClick() {
        unstable_batchedUpdates(() => {
          setCount(c => c + 1);
          setFlag(f => !f);
        });
      }
      
  3. 渲染优先级

    • 使用startTransition
      import { startTransition } from 'react';
      
      function handleInputChange(value) {
        setInputValue(value); // 紧急更新
        startTransition(() => {
          setSearchQuery(value); // 非紧急更新
        });
      }
      
  4. 避免不必要的渲染

    • 使用React.memo
      const MemoizedComponent = React.memo(function Component({ data }) {
        return <div>{data}</div>;
      });
      
    • 使用useMemo
      const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
      
    • 使用useCallback
      const memoizedCallback = useCallback(() => {
        doSomething(a, b);
      }, [a, b]);
      
网络优化
  1. HTTP/2配置

    • Nginx配置
      server {
        listen 443 ssl http2;
        http2_push /style.css;
        http2_push /app.js;
      }
      
  2. 资源预加载

    • 关键资源预加载
      <link rel="preload" href="critical.css" as="style">
      <link rel="preload" href="main.js" as="script">
      
    • 数据预取
      const { data } = useSWR('/api/data', fetcher, {
        revalidateOnFocus: false,
        revalidateOnReconnect: false
      });
      
  3. 服务端推送

    • 使用HTTP/2 Server Push
    • 缓存策略优化
      location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
      }
      
调试技巧
  1. React Profiler

    • 性能分析
      <Profiler id="App" onRender={onRenderCallback}>
        <App />
      </Profiler>
      
    • 自定义回调
      function onRenderCallback(
        id,
        phase,
        actualDuration,
        baseDuration,
        startTime,
        commitTime,
        interactions
      ) {
        console.log('Render time:', actualDuration);
      }
      
  2. Chrome DevTools

    • 性能面板
    • 内存面板
    • 网络面板
  3. Lighthouse

    • 性能评分
    • 最佳实践检查
    • 可访问性评估
性能监控
  1. Web Vitals

    • 核心指标监控
      import { getCLS, getFID, getLCP } from 'web-vitals';
      
      getCLS(console.log);
      getFID(console.log); 
      getLCP(console.log);
      
    • 自定义上报
      function sendToAnalytics(metric) {
        const body = JSON.stringify(metric);
        navigator.sendBeacon('/analytics', body);
      }
      
      getCLS(sendToAnalytics);
      getFID(sendToAnalytics);
      getLCP(sendToAnalytics);
      
  2. 错误监控

    • 错误边界
      class ErrorBoundary extends React.Component {
        state = { hasError: false };
      
        static getDerivedStateFromError(error) {
          return { hasError: true };
        }
      
        componentDidCatch(error, errorInfo) {
          logErrorToService(error, errorInfo);
        }
      
        render() {
          if (this.state.hasError) {
            return <h1>Something went wrong.</h1>;
          }
      
          return this.props.children; 
        }
      }
      
    • 全局错误捕获
      window.addEventListener('error', (event) => {
        logErrorToService(event.error);
      });
      
      window.addEventListener('unhandledrejection', (event) => {
        logErrorToService(event.reason);
      });
      
性能优化最佳实践
  1. 关键渲染路径优化

    • 最小化关键资源
    • 减少关键请求数量
    • 优化关键字节数
  2. 资源加载策略

    • 预加载关键资源
    • 延迟加载非关键资源
    • 按需加载第三方库
  3. 渲染性能优化

    • 避免不必要的重新渲染
    • 使用虚拟列表
    • 优化复杂组件
  4. 网络性能优化

    • 使用HTTP/2
    • 启用Gzip压缩
    • 使用CDN加速
  5. 内存管理

    • 清理未使用的引用
    • 避免内存泄漏
    • 使用WeakMap/WeakSet
  6. 工具使用

    • React DevTools
    • Chrome DevTools
    • Lighthouse
    • Webpack Bundle Analyzer

12.5 测试策略

12.5 测试策略

单元测试
  1. 组件测试

    import { render, screen } from '@testing-library/react';
    import Button from './Button';
    
    test('renders button with correct text', () => {
      render(<Button>Click me</Button>);
      const buttonElement = screen.getByText(/click me/i);
      expect(buttonElement).toBeInTheDocument();
    });
    
  2. Hooks测试

    import { renderHook, act } from '@testing-library/react-hooks';
    import useCounter from './useCounter';
    
    test('should increment counter', () => {
      const { result } = renderHook(() => useCounter());
    
      act(() => {
        result.current.increment();
      });
    
      expect(result.current.count).toBe(1);
    });
    
  3. 快照测试

    import renderer from 'react-test-renderer';
    import Component from './Component';
    
    test('matches snapshot', () => {
      const tree = renderer.create(<Component />).toJSON();
      expect(tree).toMatchSnapshot();
    });
    
集成测试
  1. 组件交互测试

    import { render, screen, fireEvent } from '@testing-library/react';
    import LoginForm from './LoginForm';
    
    test('submits form with correct values', () => {
      const handleSubmit = jest.fn();
      render(<LoginForm onSubmit={handleSubmit} />);
    
      fireEvent.change(screen.getByLabelText(/username/i), {
        target: { value: 'testuser' }
      });
    
      fireEvent.change(screen.getByLabelText(/password/i), {
        target: { value: 'password123' }
      });
    
      fireEvent.click(screen.getByRole('button', { name: /submit/i }));
    
      expect(handleSubmit).toHaveBeenCalledWith({
        username: 'testuser',
        password: 'password123'
      });
    });
    
  2. API集成测试

    import { render, screen, waitFor } from '@testing-library/react';
    import axios from 'axios';
    import UserList from './UserList';
    
    jest.mock('axios');
    
    test('fetches and displays users', async () => {
      axios.get.mockResolvedValue({
        data: [{ id: 1, name: 'John Doe' }]
      });
    
      render(<UserList />);
    
      await waitFor(() => {
        expect(screen.getByText(/john doe/i)).toBeInTheDocument();
      });
    });
    
E2E测试
  1. Cypress配置

    // cypress.config.js
    module.exports = {
      e2e: {
        baseUrl: 'http://localhost:3000',
        supportFile: false
      }
    };
    
  2. 页面导航测试

    describe('Navigation', () => {
      it('should navigate to about page', () => {
        cy.visit('/');
        cy.get('a[href*="about"]').click();
        cy.url().should('include', '/about');
        cy.get('h1').contains('About Page');
      });
    });
    
  3. 表单提交测试

    describe('Login Form', () => {
      it('should submit form', () => {
        cy.visit('/login');
        cy.get('#username').type('testuser');
        cy.get('#password').type('password123');
        cy.get('form').submit();
        cy.url().should('include', '/dashboard');
      });
    });
    
测试覆盖率
  1. Jest配置

    {
      "collectCoverage": true,
      "coverageReporters": ["text", "lcov"],
      "coverageThreshold": {
        "global": {
          "branches": 80,
          "functions": 80,
          "lines": 80,
          "statements": 80
        }
      }
    }
    
  2. 生成覆盖率报告

    npm test -- --coverage
    
  3. 查看HTML报告

    open coverage/lcov-report/index.html
    
测试最佳实践
  1. 测试金字塔

    • 70% 单元测试
    • 20% 集成测试
    • 10% E2E测试
  2. 测试命名规范

    • 描述性测试名称
    • Given-When-Then模式
    • 避免使用"should"
  3. 测试数据管理

    • 使用工厂函数生成测试数据
    • 避免硬编码
    • 使用faker.js生成随机数据
  4. 测试隔离

    • 每个测试独立运行
    • 使用beforeEach/afterEach清理状态
    • 避免测试间依赖

13. 部署与运维

13.1 部署策略

静态资源部署
  • CDN加速配置
    # 使用AWS CloudFront配置示例
    aws cloudfront create-distribution \
      --origin-domain-name your-bucket.s3.amazonaws.com \
      --default-root-object index.html \
      --enabled
    
  • 缓存策略优化
    • 设置Cache-Control头
    • 使用ETag进行缓存验证
    • 配置合理的max-age
  • 版本控制方案
    • 基于文件内容hash生成文件名
    • 使用webpack配置示例:
      output: {
        filename: '[name].[contenthash].js',
        path: path.resolve(__dirname, 'dist')
      }
      
服务端渲染部署
  • Node.js服务器配置
    • 使用PM2进行进程管理
      pm2 start server.js -i max
      pm2 save
      pm2 startup
      
    • 配置Nginx反向代理
      server {
        listen 80;
        server_name yourdomain.com;
      
        location / {
          proxy_pass http://localhost:3000;
          proxy_http_version 1.1;
          proxy_set_header Upgrade $http_upgrade;
          proxy_set_header Connection 'upgrade';
          proxy_set_header Host $host;
          proxy_cache_bypass $http_upgrade;
        }
      }
      
  • 负载均衡配置
    • 使用Nginx实现负载均衡
      upstream nodejs {
        server 127.0.0.1:3000;
        server 127.0.0.1:3001;
        server 127.0.0.1:3002;
      }
      
    • 配置健康检查
      location /health {
        return 200 'healthy';
      }
      
  • 自动扩展策略
    • 基于CPU使用率自动扩展
    • 基于请求量自动扩展
    • 配置自动扩展组(AWS示例)
      aws autoscaling create-auto-scaling-group \
        --auto-scaling-group-name my-asg \
        --min-size 2 \
        --max-size 10 \
        --desired-capacity 2 \
        --vpc-zone-identifier "subnet-xxxxxx"
      

13.2 CI/CD 配置

  • 持续集成
    • 代码质量检查
    • 单元测试
    • 构建验证
  • 持续部署
    • 自动化部署流程
    • 蓝绿部署
    • 回滚机制

13.3 监控与告警

  • 性能监控
    • 页面加载时间
    • API响应时间
    • 错误率
  • 日志管理
    • 集中式日志收集
    • 日志分级
    • 日志分析
  • 告警系统
    • 关键指标阈值
    • 多渠道通知
    • 告警分级

13.4 安全策略

  • 前端安全
    • XSS防护
    • CSRF防护
    • CSP配置
  • 部署安全
    • HTTPS强制
    • 访问控制
    • 安全扫描

13.5 性能优化

资源优化
  1. 图片优化

    • 使用WebP格式
    • 响应式图片加载
      <picture>
        <source srcSet="image.webp" type="image/webp" />
        <img src="image.jpg" alt="Example" />
      </picture>
      
    • 图片懒加载
      <img 
        src="placeholder.jpg" 
        data-src="real-image.jpg" 
        className="lazyload"
        alt="Example"
      />
      
  2. 代码优化

    • Webpack配置示例
      optimization: {
        splitChunks: {
          chunks: 'all',
          minSize: 20000,
          maxSize: 50000,
        },
        minimize: true,
        minimizer: [new TerserPlugin()],
      }
      
    • Tree Shaking配置
      {
        "sideEffects": false
      }
      
  3. 字体优化

    • 使用font-display: swap
    • 子集化字体文件
    • 预加载关键字体
      <link 
        rel="preload" 
        href="/fonts/Inter.woff2" 
        as="font" 
        type="font/woff2" 
        crossorigin
      />
      
网络优化
  1. HTTP/2配置

    • Nginx示例
      server {
        listen 443 ssl http2;
        http2_push /style.css;
        http2_push /app.js;
      }
      
  2. 资源预加载

    • 关键资源预加载
      <link rel="preload" href="critical.css" as="style">
      <link rel="preload" href="main.js" as="script">
      
    • 数据预取
      const data = useSWR('/api/data', fetcher, {
        revalidateOnFocus: false,
        revalidateOnReconnect: false
      });
      
  3. 服务端推送

    • 使用HTTP/2 Server Push
    • 缓存策略优化
      location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
      }
      
调试技巧
  1. React DevTools使用

    • 组件性能分析
    • 状态调试
    • 组件树检查
  2. 性能监控

    • 使用React Profiler
      <Profiler id="App" onRender={onRenderCallback}>
        <App />
      </Profiler>
      
    • Lighthouse性能分析
    • Web Vitals监控
      import { getCLS, getFID, getLCP } from 'web-vitals';
      
      getCLS(console.log);
      getFID(console.log); 
      getLCP(console.log);
      
  3. 内存泄漏检测

    • 使用Chrome DevTools Memory面板
    • 检测未清理的副作用
      useEffect(() => {
        const subscription = someObservable.subscribe();
        return () => subscription.unsubscribe();
      }, []);
      

14. 总结

React 作为现代前端开发的核心技术之一,具有强大的功能和活跃的社区。通过本指南,您已经掌握了 React 的核心概念和常用技巧。建议通过实际项目来巩固所学知识,并持续关注 React 生态的最新发展。


http://www.kler.cn/a/511755.html

相关文章:

  • 【博客之星2024年度总评选】年度回望:我的博客之路与星光熠熠
  • Kinova仿生机械臂Gen3搭载BOTA 力矩传感器SeneOne:彰显机器人触觉 AI 与六维力传感的融合力量
  • kubernetes学习-Service(七)
  • 利用R计算一般配合力(GCA)和特殊配合力(SCA)
  • 图论DFS:黑红树
  • OpenMP并行编程实例系列2 —— 并行结构
  • 合合信息DocFlow产品解析与体验:人人可搭建的AI自动化单据处理工作流
  • UE虚幻引擎No Google Play Store Key:No OBB found报错如何处理?
  • 【spring boot统一功能处理】拦截器
  • 梯度提升决策树树(GBDT)公式推导
  • 【面试题】JVM部分[2025/1/13 ~ 2025/1/19]
  • pytest+playwright落地实战大纲
  • leetcode242-有效字母异位词
  • RoCE网络及其协议栈详解(没有中间商赚差价的网络)
  • 快速排序练习
  • Linux:生产者消费者模型
  • 第 3 章 核心处理层(上)
  • Bash 中 nohup 与 的区别及用法解析
  • leetcode416.分割等和子集
  • nginx作为下载服务器配置
  • Python人脸识别库DeepFace使用教程及源码解析
  • imbinarize函数用法详解与示例
  • python 基础类json和csv
  • 深入剖析iOS网络优化策略,提升App性能
  • 【LC】2239. 找到最接近 0 的数字
  • Node.js 写一个登录中间件