前端面试题之React篇

301 阅读13分钟

React核心知识与项目设计摘要

React面试题要点

核心概念

  • React基础: 虚拟DOM、组件化、单向数据流、JSX语法
  • 组件类型: 函数组件(推荐)与类组件的区别与应用场景
  • 生命周期: 挂载、更新、卸载三大阶段及各阶段方法的应用

Hooks相关

  • 基础Hooks: useState管理状态、useEffect处理副作用
  • 性能优化: useCallback缓存函数、useMemo缓存值、自定义Hook复用逻辑
  • 状态管理: useReducer处理复杂状态、useContext跨层级传递数据

性能优化

  • 减少渲染: React.memo、shouldComponentUpdate、列表渲染key优化
  • 代码分割: React.lazy与Suspense实现按需加载
  • 数据获取: SWR/React Query管理服务端状态

架构设计

  • 状态管理: Context/Redux/Zustand等方案的选择与应用
  • 路由设计: React Router实现SPA导航与路由守卫
  • 组件设计模式: HOC、Compound Components等高级组件模式

React新特性

  • Concurrent Mode: 并发渲染、自动批处理
  • Server Components: 服务端组件减少客户端负担
  • Suspense: 声明式数据加载与错误边界处理

项目设计方案

规模化策略

  • 小型项目: Context+函数组件、轻量级技术栈、简单目录结构
  • 中型项目: 特性驱动开发、Zustand/Jotai状态管理、组件分层
  • 大型项目: 微前端架构、Monorepo管理、DDD领域驱动设计

工程化实践

  • 代码质量: TypeScript类型检查、ESLint规范、Jest自动化测试
  • 项目结构: 按功能/页面组织代码、原子设计组件拆分
  • 持续集成: Husky提交检查、CI/CD自动部署

性能与体验

  • 首屏优化: SSR/SSG提升加载体验、代码分割降低包体积
  • 用户体验: 虚拟列表处理长列表、Skeleton提升加载感知
  • 监控系统: 错误追踪与性能监控方案

核心概念

1. React是什么?它的主要特点是什么?

答案: React是一个用于构建用户界面的JavaScript库。主要特点包括:

  • 组件化开发
  • 虚拟DOM,高效更新
  • 单向数据流
  • JSX语法支持
  • 声明式编程

2. 什么是虚拟DOM?它如何提高性能?

答案: 虚拟DOM是React内存中DOM的表示。React通过比较前后两次虚拟DOM的差异(Diffing算法),最小化实际DOM操作,从而提高性能。

最佳实践: 避免直接操作DOM,让React处理DOM更新。使用shouldComponentUpdateReact.memo减少不必要的渲染。

3. 类组件与函数组件的区别?

答案:

  • 类组件基于ES6 class,可使用生命周期方法和state
  • 函数组件是纯函数,使用Hooks管理状态和副作用
  • 函数组件性能更好,代码更简洁

最佳实践: 优先使用函数组件和Hooks,除非有特殊需求必须使用类组件。

Hooks相关

4. 解释useState和useEffect的用途

答案:

  • useState:在函数组件中添加状态
  • useEffect:处理副作用,如API调用、订阅、手动DOM操作等

最佳实践:

// 正确用法
const [count, setCount] = useState(0);
useEffect(() => {
  document.title = `点击了${count}次`;
  return () => { /* 清理函数 */ };
}, [count]); // 依赖数组

5. useCallback与useMemo的区别?

答案:

  • useCallback:缓存函数引用,避免不必要的渲染
  • useMemo:缓存计算结果,避免昂贵计算的重复执行

最佳实践:

// 函数缓存
const handleClick = useCallback(() => {
  console.log(count);
}, [count]);

// 值缓存
const doubleCount = useMemo(() => {
  return expensiveCalculation(count);
}, [count]);

6. 自定义Hook的作用是什么?

答案: 自定义Hook用于提取组件逻辑到可重用的函数中,遵循use命名约定,可以调用其他Hook。

最佳实践:

function useWindowSize() {
  const [size, setSize] = useState({ width: 0, height: 0 });
  
  useEffect(() => {
    const handleResize = () => {
      setSize({ width: window.innerWidth, height: window.innerHeight });
    };
    window.addEventListener('resize', handleResize);
    handleResize(); // 初始化
    return () => window.removeEventListener('resize', handleResize);
  }, []);
  
  return size;
}

性能优化

7. React如何优化性能?

答案:

  • 使用React.memo避免不必要的重新渲染
  • 使用useCallbackuseMemo缓存函数和值
  • 使用shouldComponentUpdatePureComponent优化类组件
  • 适当拆分组件,实现精确更新
  • 使用React.lazySuspense实现代码分割

最佳实践:

const MemoizedComponent = React.memo(MyComponent, (prevProps, nextProps) => {
  // 返回true表示不需要重新渲染
  return prevProps.id === nextProps.id;
});

8. 列表渲染为什么需要key,它的作用是什么?

答案: key帮助React识别哪些元素改变了,如增加或删除,帮助DOM高效更新。key应该是稳定、唯一、可预测的。

最佳实践:

// 好的做法
{items.map(item => <Item key={item.id} {...item} />)}

// 避免使用索引作为key,除非列表是静态的、不会重新排序

状态管理

9. Context API的使用场景是什么?

答案: Context用于多层组件共享数据,避免层层Props传递(prop drilling)。适用于主题、语言、用户认证等全局状态。

最佳实践:

// 创建Context
const ThemeContext = React.createContext('light');

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

// 消费Context
function ThemedButton() {
  const theme = useContext(ThemeContext);
  return <button className={theme}>按钮</button>;
}

10. Redux与React Context的区别?

答案:

  • Redux提供全局状态管理,有严格的单向数据流
  • Context主要用于避免props传递,处理局部状态共享
  • Redux适合复杂应用,有时间旅行调试等工具
  • Context适合中小型应用或特定场景

最佳实践: 先考虑使用Context和useReducer,当状态逻辑复杂时再考虑引入Redux。

路由

11. React Router的工作原理?

答案: React Router通过匹配URL和路由配置,渲染对应的组件。它使用History API在不刷新页面的情况下更新URL。

最佳实践:

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

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/users/:userId" element={<UserProfile />} />
      </Routes>
    </BrowserRouter>
  );
}

其他热门问题

12. React Fiber是什么?

答案: Fiber是React 16引入的新调和算法,支持任务分割和优先级,可中断渲染过程,提高响应性,为异步渲染奠定基础。

13. 如何处理React中的表单?

答案:

  • 受控组件:表单元素值由React状态控制
  • 非受控组件:表单元素值存在DOM中,通过ref访问

最佳实践:

// 受控组件
function Form() {
  const [value, setValue] = useState('');
  
  const handleChange = (e) => {
    setValue(e.target.value);
  };
  
  return <input value={value} onChange={handleChange} />;
}

14. React 18有哪些新特性?

答案:

  • 并发渲染
  • 自动批处理
  • Suspense SSR支持
  • useTransition/useDeferredValue Hooks
  • 新的Root API

最佳实践:

// 使用新的Root API
import { createRoot } from 'react-dom/client';
const root = createRoot(document.getElementById('root'));
root.render(<App />);

// 使用useTransition处理低优先级更新
const [isPending, startTransition] = useTransition();
startTransition(() => {
  setSearchResults(filterItems(input));
});

15. 如何实现React组件的条件渲染?

答案: 使用逻辑运算符、三元表达式或提前return实现条件渲染。

最佳实践:

// 使用&&运算符
{isLoggedIn && <UserProfile />}

// 使用三元表达式
{isLoggedIn ? <LogoutButton /> : <LoginButton />}

// 函数内提前返回
if (!items.length) return <EmptyState />;

生命周期与更新机制

16. React组件的生命周期有哪些阶段?

答案: React 16.3后的生命周期分为三个阶段:

  • 挂载: constructor → getDerivedStateFromProps → render → componentDidMount
  • 更新: getDerivedStateFromProps → shouldComponentUpdate → render → getSnapshotBeforeUpdate → componentDidUpdate
  • 卸载: componentWillUnmount

最佳实践: 在函数组件中使用useEffect替代生命周期方法。

17. React中setState是同步还是异步的?

答案: setState在React事件处理和生命周期中是批量异步更新的,在setTimeout、Promise等原生事件中是同步的。React 18中,所有setState都会自动批处理。

最佳实践:

// 依赖前一个状态
setState(prevState => ({
  counter: prevState.counter + 1
}));

// 需要立即使用更新后的状态时,使用useEffect
useEffect(() => {
  console.log(count); // 这里的count是更新后的值
}, [count]);

高级Hooks

18. useReducer的使用场景是什么?

答案: useReducer适用于复杂状态逻辑,尤其是状态之间有依赖关系或下一个状态依赖于之前的状态。

最佳实践:

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return {count: state.count + 1};
    case 'decrement':
      return {count: state.count - 1};
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, {count: 0});
  return (
    <>
      Count: {state.count}
      <button onClick={() => dispatch({type: 'increment'})}>+</button>
      <button onClick={() => dispatch({type: 'decrement'})}>-</button>
    </>
  );
}

19. useRef的作用是什么?

答案: useRef主要有两个用途:

  • 访问DOM节点或React元素
  • 保存不触发重新渲染的可变值

最佳实践:

// 访问DOM节点
const inputRef = useRef(null);
useEffect(() => {
  inputRef.current.focus();
}, []);
return <input ref={inputRef} />;

// 保存可变值
const renderCountRef = useRef(0);
useEffect(() => {
  renderCountRef.current += 1;
});

架构与设计模式

20. 什么是受控组件和非受控组件?

答案:

  • 受控组件:表单数据由React组件控制,通过props和state实现
  • 非受控组件:表单数据由DOM元素自身控制,通过ref获取值

最佳实践:

// 受控组件
function ControlledForm() {
  const [value, setValue] = useState('');
  return <input value={value} onChange={e => setValue(e.target.value)} />;
}

// 非受控组件
function UncontrolledForm() {
  const inputRef = useRef(null);
  const handleSubmit = () => {
    console.log(inputRef.current.value);
  };
  return <input ref={inputRef} defaultValue="默认值" />;
}

21. 什么是高阶组件(HOC)?

答案: 高阶组件是接收一个组件并返回一个新组件的函数,用于复用组件逻辑。

最佳实践:

// 高阶组件示例 - 添加日志功能
function withLogging(Component) {
  return function LoggedComponent(props) {
    useEffect(() => {
      console.log(`组件${Component.name}已挂载`);
      return () => console.log(`组件${Component.name}已卸载`);
    }, []);
    
    return <Component {...props} />;
  };
}

// 使用HOC
const EnhancedButton = withLogging(Button);

性能优化进阶

22. 如何处理React中的大型列表渲染?

答案: 使用虚拟滚动技术,只渲染可视区域内的元素,如使用react-window或react-virtualized库。

最佳实践:

import { FixedSizeList } from 'react-window';

function VirtualizedList({ items }) {
  const Row = ({ index, style }) => (
    <div style={style}>
      {items[index].name}
    </div>
  );
  
  return (
    <FixedSizeList
      height={500}
      width={300}
      itemCount={items.length}
      itemSize={50}
    >
      {Row}
    </FixedSizeList>
  );
}

23. 如何避免不必要的重新渲染?

答案:

  • 使用shouldComponentUpdate或React.memo
  • 使用useCallback和useMemo
  • 组件拆分,分离变化和不变的部分
  • 使用React.lazy进行代码分割

最佳实践:

// 避免传递新对象导致的不必要渲染
const memoizedValue = useMemo(() => ({ id, name }), [id, name]);
return <ChildComponent data={memoizedValue} />;

服务端渲染与新特性

24. 什么是服务端渲染(SSR)?它有什么优势?

答案: SSR是在服务端生成HTML,然后发送到客户端。优势包括:

  • 更好的SEO
  • 更快的首屏加载
  • 对低性能设备更友好
  • 改善核心Web指标

最佳实践: 使用Next.js或Remix等框架实现SSR。

25. React中的错误边界是什么?

答案: 错误边界是捕获子组件树JavaScript错误的组件,可以记录错误并显示备用UI。

最佳实践:

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    return { hasError: true };
  }
  
  componentDidCatch(error, errorInfo) {
    // 记录错误到服务
    logErrorToService(error, errorInfo);
  }
  
  render() {
    if (this.state.hasError) {
      return <h1>出错了,请稍后重试。</h1>;
    }
    return this.props.children;
  }
}

// 使用错误边界
<ErrorBoundary>
  <MyComponent />
</ErrorBoundary>

TypeScript与React

26. React中如何使用TypeScript类型?

答案: 使用TypeScript定义组件props、state和事件处理函数的类型,提高代码质量。

最佳实践:

interface ButtonProps {
  text: string;
  onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
  variant?: 'primary' | 'secondary' | 'danger';
}

const Button: React.FC<ButtonProps> = ({ 
  text, 
  onClick, 
  variant = 'primary' 
}) => {
  return (
    <button 
      className={`btn btn-${variant}`} 
      onClick={onClick}
    >
      {text}
    </button>
  );
};

27. 什么是React的Suspense?

答案: Suspense让组件在渲染前等待某些操作完成,比如数据获取。在等待期间可以显示fallback内容。

最佳实践:

// 代码分割
const LazyComponent = React.lazy(() => import('./LazyComponent'));

function MyComponent() {
  return (
    <Suspense fallback={<div>加载中...</div>}>
      <LazyComponent />
    </Suspense>
  );
}

// React 18中数据获取
function ProfilePage() {
  return (
    <Suspense fallback={<Spinner />}>
      <ProfileDetails />
    </Suspense>
  );
}

测试与调试

28. 如何测试React组件?

答案: 使用Jest和React Testing Library进行组件测试,Cypress或Playwright进行端到端测试。

最佳实践:

// 使用React Testing Library
import { render, screen, fireEvent } from '@testing-library/react';

test('点击按钮增加计数', () => {
  render(<Counter />);
  const button = screen.getByText('增加');
  fireEvent.click(button);
  expect(screen.getByText('计数: 1')).toBeInTheDocument();
});

29. useEffect的清理函数有什么作用?

答案: useEffect的清理函数用于防止内存泄漏,清理订阅、定时器等副作用。

最佳实践:

useEffect(() => {
  const subscription = api.subscribe(data => {
    setData(data);
  });
  
  // 清理函数
  return () => {
    subscription.unsubscribe();
  };
}, []);

30. React中如何实现代码分割?

答案: 使用React.lazy和Suspense实现代码分割,按需加载组件,减小打包体积。

最佳实践:

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

function App() {
  return (
    <Router>
      <Suspense fallback={<div>加载中...</div>}>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/about" element={<About />} />
        </Routes>
      </Suspense>
    </Router>
  );
}

状态管理进阶

31. Redux中间件的作用是什么?

答案: Redux中间件提供了位于action被发起之后、到达reducer之前的第三方扩展点,用于处理异步操作、日志记录、崩溃报告等副作用。

最佳实践:

// redux-thunk中间件使用示例
const fetchUsers = () => {
  return async (dispatch) => {
    dispatch({ type: 'FETCH_USERS_REQUEST' });
    try {
      const response = await api.getUsers();
      dispatch({ type: 'FETCH_USERS_SUCCESS', payload: response.data });
    } catch (error) {
      dispatch({ type: 'FETCH_USERS_FAILURE', error });
    }
  };
};

32. React中如何实现状态不可变性?

答案: 使用扩展运算符、Object.assign()、数组方法如map/filter/concat或使用Immer库确保状态不可变性。

最佳实践:

// 不可变更新对象
setState(prevState => ({
  ...prevState,
  user: {
    ...prevState.user,
    name: 'Zhang San'
  }
}));

// 使用Immer库
import produce from 'immer';

setState(produce(draft => {
  draft.user.name = 'Zhang San';
}));

33. Recoil与Redux有什么不同?

答案: Recoil是Facebook推出的状态管理库,相比Redux:

  • API更简单,学习成本低
  • 原生支持React Concurrent Mode
  • 支持Atom级别的细粒度更新
  • 使用Selector实现派生状态

最佳实践:

import { atom, selector, useRecoilState, useRecoilValue } from 'recoil';

// 定义atom
const counterState = atom({
  key: 'counterState',
  default: 0,
});

// 定义selector
const doubleCountState = selector({
  key: 'doubleCountState',
  get: ({get}) => {
    return get(counterState) * 2;
  },
});

// 组件中使用
function Counter() {
  const [count, setCount] = useRecoilState(counterState);
  const doubleCount = useRecoilValue(doubleCountState);
  
  return (
    <div>
      <button onClick={() => setCount(count + 1)}>增加</button>
      <div>计数: {count}</div>
      <div>双倍: {doubleCount}</div>
    </div>
  );
}

React生态系统

34. React Hook Form和Formik的区别是什么?

答案: 两者都是React表单库,但有如下区别:

  • React Hook Form更轻量,强调无控组件,性能更好
  • Formik基于受控组件,API更直观
  • React Hook Form使用ref获取值,减少重新渲染
  • Formik提供更多的表单状态和辅助函数

最佳实践:

// React Hook Form
import { useForm } from "react-hook-form";

function HookForm() {
  const { register, handleSubmit, errors } = useForm();
  
  const onSubmit = data => console.log(data);
  
  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register("name", { required: true })} />
      {errors.name && <span>姓名必填</span>}
      <button type="submit">提交</button>
    </form>
  );
}

35. 如何使用CSS-in-JS方案?

答案: 通过CSS-in-JS库如styled-components或emotion,可以在React组件内直接编写样式,实现样式的组件化和动态化。

最佳实践:

// styled-components
import styled from 'styled-components';

const Button = styled.button`
  background: ${props => props.primary ? 'blue' : 'white'};
  color: ${props => props.primary ? 'white' : 'blue'};
  border-radius: 4px;
  padding: 8px 16px;
  transition: all 0.3s ease;
  
  &:hover {
    opacity: 0.8;
  }
`;

function App() {
  return <Button primary>点击我</Button>;
}

性能优化高级技巧

36. 什么是React.memo,它与PureComponent有什么区别?

答案:

  • React.memo是高阶组件,用于函数组件的性能优化,通过浅比较props确定是否重新渲染
  • PureComponent是类组件,通过浅比较props和state确定是否重新渲染
  • 两者都通过浅比较实现优化,但适用于不同类型的组件

最佳实践:

// React.memo
const MyComponent = React.memo(function MyComponent(props) {
  /* 渲染使用props */
  return <div>{props.name}</div>;
});

// 自定义比较函数
const MyComponent = React.memo(
  function MyComponent(props) {
    /* 渲染使用props */
    return <div>{props.name}</div>;
  },
  (prevProps, nextProps) => {
    // 返回true表示不需要重新渲染
    return prevProps.id === nextProps.id;
  }
);

37. 如何解决React中的重渲染问题?

答案: 重渲染问题的解决方法包括:

  • 使用React.memo包装纯组件
  • 使用useCallback避免重复创建函数
  • 使用useMemo避免重复计算
  • 适当拆分组件,隔离变化
  • 使用State提升精确控制更新范围

最佳实践:

function ParentComponent() {
  const [count, setCount] = useState(0);
  const [text, setText] = useState('');
  
  // 缓存回调函数
  const handleClick = useCallback(() => {
    setCount(c => c + 1);
  }, []);
  
  return (
    <>
      <input value={text} onChange={(e) => setText(e.target.value)} />
      <ExpensiveChild count={count} onClick={handleClick} />
    </>
  );
}

// 使用React.memo避免text改变时重渲染
const ExpensiveChild = React.memo(({ count, onClick }) => {
  console.log('ExpensiveChild render');
  return <button onClick={onClick}>点击了 {count} 次</button>;
});

React架构与设计

38. 如何实现React组件的按需加载?

答案: 使用React.lazy和动态import实现组件按需加载,配合Suspense显示加载状态。

最佳实践:

import React, { Suspense, lazy } from 'react';

// 按需加载路由组件
const UserProfile = lazy(() => import('./UserProfile'));
const UserSettings = lazy(() => import('./UserSettings'));

function App() {
  return (
    <div>
      <Suspense fallback={<div>加载中...</div>}>
        <Router>
          <Routes>
            <Route path="/profile" element={<UserProfile />} />
            <Route path="/settings" element={<UserSettings />} />
          </Routes>
        </Router>
      </Suspense>
    </div>
  );
}

39. React中如何实现组件通信?

答案: React组件通信方式包括:

  • Props传递(父子组件)
  • Context API(跨层级组件)
  • 状态管理库(Redux/Mobx)
  • 发布-订阅模式(EventEmitter)
  • 自定义事件总线

最佳实践:

// 1. Props传递
function Parent() {
  const [message, setMessage] = useState('');
  
  const handleMessage = (msg) => {
    setMessage(msg);
  };
  
  return <Child onSendMessage={handleMessage} parentMessage={message} />;
}

// 2. Context API
const MessageContext = createContext();

function MessageProvider({ children }) {
  const [message, setMessage] = useState('');
  
  return (
    <MessageContext.Provider value={{ message, setMessage }}>
      {children}
    </MessageContext.Provider>
  );
}

function DeepChild() {
  const { message, setMessage } = useContext(MessageContext);
  return (
    <>
      <div>{message}</div>
      <button onClick={() => setMessage('新消息')}>发送消息</button>
    </>
  );
}

40. 什么是Compound Components设计模式?

答案: Compound Components是React中的一种设计模式,通过创建具有隐式状态共享的子组件集合,实现更灵活的组件组合。

最佳实践:

function Tabs({ children, defaultIndex = 0 }) {
  const [activeIndex, setActiveIndex] = useState(defaultIndex);
  
  const context = {
    activeIndex,
    setActiveIndex
  };
  
  return (
    <TabsContext.Provider value={context}>
      {children}
    </TabsContext.Provider>
  );
}

function TabList({ children }) {
  return <div className="tab-list">{children}</div>;
}

function Tab({ children, index }) {
  const { activeIndex, setActiveIndex } = useContext(TabsContext);
  return (
    <div 
      className={activeIndex === index ? 'tab active' : 'tab'}
      onClick={() => setActiveIndex(index)}
    >
      {children}
    </div>
  );
}

// 使用方式
<Tabs>
  <TabList>
    <Tab index={0}>标签一</Tab>
    <Tab index={1}>标签二</Tab>
  </TabList>
  <TabPanels>
    <TabPanel>内容一</TabPanel>
    <TabPanel>内容二</TabPanel>
  </TabPanels>
</Tabs>

React 18与新特性

41. React 18中的并发特性是什么?

答案: React 18引入了并发渲染机制,主要特性包括:

  • 自动批处理:更智能地批量更新状态
  • Transitions API:区分紧急和非紧急更新
  • Suspense SSR:流式服务端渲染
  • 并发特性:允许React中断、恢复和放弃渲染

最佳实践:

// 使用useTransition区分紧急和非紧急更新
const [isPending, startTransition] = useTransition();

// 紧急更新
setInputValue(input);

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

// 显示加载状态
{isPending && <Spinner />}

// 使用useDeferredValue获取延迟值
const deferredQuery = useDeferredValue(query);

42. 什么是Server Components?

答案: React Server Components是一种新组件类型,完全在服务器上运行,不需要JavaScript bundle,可以访问服务器资源,减少客户端JavaScript负载。

最佳实践:

// 服务器组件(不使用useState/useEffect等客户端特性)
// ServerComponent.server.js
import { db } from './database';

async function ServerComponent({ id }) {
  const data = await db.query(`SELECT * FROM items WHERE id = ${id}`);
  return <div>{data.title}</div>;
}

// 客户端组件
// ClientComponent.client.js
'use client';

import { useState } from 'react';

export default function ClientComponent() {
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(count + 1)}>{count}</button>;
}

实战问题

43. 如何管理React应用中的全局状态?

答案: 依据应用规模和复杂度选择合适的方案:

  • 小型应用:使用React Context + useReducer
  • 中型应用:考虑使用Zustand、Jotai等轻量库
  • 大型应用:使用Redux、MobX等成熟方案
  • 特定需求:考虑React Query、SWR处理服务端状态

最佳实践:

// Zustand示例
import create from 'zustand';

const useStore = create((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
  reset: () => set({ count: 0 }),
}));

function Counter() {
  const { count, increment } = useStore();
  return (
    <div>
      <p>计数: {count}</p>
      <button onClick={increment}>增加</button>
    </div>
  );
}

44. 如何处理React中的表单验证?

答案: 表单验证可使用自定义Hooks、第三方库如Formik/React Hook Form,或使用HTML5内置验证API结合自定义逻辑。

最佳实践:

// 自定义Hook进行表单验证
function useFormValidation(initialValues, validate) {
  const [values, setValues] = useState(initialValues);
  const [errors, setErrors] = useState({});
  const [isSubmitting, setIsSubmitting] = useState(false);
  
  useEffect(() => {
    if (isSubmitting) {
      const noErrors = Object.keys(errors).length === 0;
      if (noErrors) {
        // 提交表单
        console.log('表单提交', values);
        setIsSubmitting(false);
      } else {
        setIsSubmitting(false);
      }
    }
  }, [errors, isSubmitting, values]);
  
  const handleChange = (event) => {
    setValues({
      ...values,
      [event.target.name]: event.target.value
    });
  };
  
  const handleSubmit = (event) => {
    event.preventDefault();
    const validationErrors = validate(values);
    setErrors(validationErrors);
    setIsSubmitting(true);
  };
  
  return {
    values,
    errors,
    handleChange,
    handleSubmit
  };
}

45. 如何优化React应用的首屏加载时间?

答案: 优化首屏加载时间的方法包括:

  • 代码分割和按需加载
  • 路由级懒加载
  • 预加载关键资源
  • 使用服务端渲染
  • 优化图片和媒体资源
  • 开启Gzip/Brotli压缩
  • 使用缓存策略

最佳实践:

// 预加载路由组件
const ProfilePage = lazy(() => import('./routes/Profile'));

// 在用户hover链接时预加载
function NavLink({ to, children }) {
  const prefetchProfile = () => {
    // 预加载组件
    import('./routes/Profile');
  };
  
  return (
    <Link 
      to={to} 
      onMouseEnter={prefetchProfile}
      onFocus={prefetchProfile}
    >
      {children}
    </Link>
  );
}

React实际项目最佳设计方案

项目规模划分标准

规模特征团队规模页面/组件数量
小型单一功能、周期短1-3人10-20页面
中型多个模块、周期中等3-8人20-100页面
大型多业务线、长期维护8人以上100+页面

小型项目最佳设计

技术选型

  • 框架:React + Vite (快速启动)
  • 样式:Tailwind CSS/CSS Modules
  • 状态管理:React Context + useReducer
  • 路由:React Router v6
  • 请求:React Query/SWR
  • 表单:React Hook Form (轻量)

项目结构

src/
├── components/      # 共享组件
├── pages/           # 页面组件
├── hooks/           # 自定义hooks
├── context/         # Context状态
├── api/             # API请求
├── utils/           # 工具函数
└── assets/          # 静态资源

最佳实践

  • 使用函数组件和Hooks
  • 组件粒度适中,避免过度拆分
  • 简单状态使用Context,避免引入Redux
  • 使用Suspense和ErrorBoundary处理加载和错误状态
  • 小型组件库如shadcn/ui满足UI需求

中型项目最佳设计

技术选型

  • 框架:React + Next.js
  • 样式:Styled Components/Emotion
  • 状态管理:Zustand/Jotai/Redux Toolkit
  • 路由:Next.js内置路由
  • 请求:React Query + Axios拦截器
  • 表单:Formik/React Hook Form + Yup/Zod验证
  • 组件库:MUI/Ant Design

项目结构

src/
├── components/             # 组件(原子设计)
│   ├── atoms/             # 原子组件
│   ├── molecules/         # 分子组件
│   └── organisms/         # 有机体组件
├── features/              # 按功能模块组织
│   ├── auth/              # 认证功能
│   └── dashboard/         # 仪表盘功能
├── pages/                 # 页面组件/路由
├── hooks/                 # 自定义hooks
├── store/                 # 状态管理
├── services/              # API服务
├── utils/                 # 工具函数
└── assets/                # 静态资源

最佳实践

  • 组件分层,实现UI与逻辑分离
  • 采用特性驱动开发(FDD)模式,按功能模块组织文件
  • 公共状态使用Zustand,局部状态使用useState/useReducer
  • 实现权限控制与路由守卫
  • API请求集中管理,使用适配器模式
  • 组件文档与Storybook集成

大型项目最佳设计

技术选型

  • 框架:React + Next.js/Remix
  • 架构:微前端 (Module Federation/Qiankun)
  • 样式:设计系统 + CSS-in-JS
  • 状态管理:Redux Toolkit + RTK Query
  • 路由:基于权限的动态路由
  • 数据获取:GraphQL + Apollo Client/Relay
  • 类型检查:TypeScript(严格模式)
  • 测试:Jest + React Testing Library + Cypress

项目结构

apps/                      # 多应用/微前端
  ├── main/                # 主应用
  ├── admin/               # 管理后台
  └── marketing/           # 营销模块
packages/                  # 共享包
  ├── ui/                  # UI组件库
  ├── hooks/               # 业务Hooks库
  ├── api-client/          # API客户端
  ├── utils/               # 工具函数
  └── eslint-config/       # 共享ESLint配置

最佳实践

  • 采用Monorepo管理多个应用与共享库
  • 实现自己的设计系统,确保品牌一致性
  • 应用DDD领域驱动设计思想组织代码
  • 中间件处理复杂异步流程(Redux-Saga/Redux-Observable)
  • 强类型约束,减少运行时错误
  • 完善的测试覆盖(单元测试、集成测试、E2E测试)
  • 性能监控与错误追踪系统
  • 国际化与多语言支持
  • CI/CD自动化部署流水线

通用最佳实践

代码质量保障

  1. 代码规范:ESLint + Prettier + Husky
  2. 类型检查:TypeScript
  3. 测试:Jest/Vitest + React Testing Library
  4. Code Review:PR模板与代码审查清单

性能优化策略

  1. 代码分割:按路由/组件懒加载
  2. 自定义Hooks优化:避免重复渲染
  3. 使用memo/useCallback/useMemo:缓存优化
  4. 虚拟列表:处理长列表渲染

可访问性与SEO

  1. 可访问性:ARIA属性、语义化HTML、键盘支持
  2. SSR/SSG:提升首屏加载与SEO
  3. 元数据管理:动态Title与Meta标签

项目文档与知识管理

  1. 组件文档:Storybook展示组件用法
  2. API文档:Swagger/OpenAPI规范
  3. 架构决策记录:记录技术决策与演进

选型决策表

需求/规模小型中型大型
构建工具ViteNext.jsTurborepo + Next.js
状态管理ContextZustand/JotaiRedux Toolkit
样式方案TailwindStyled Components设计系统
数据获取SWRReact QueryGraphQL
部署策略静态部署Docker容器K8s集群
缓存策略浏览器缓存服务端缓存多级缓存

根据项目复杂度和团队规模选择适合的技术栈,避免过度设计,从小做起,逐步演进。