前端面试题库 - React框架篇

一、React核心概念

1. React的核心思想与特点

核心思想:

  • 组件化:UI拆分为独立、可复用的组件
  • 声明式:描述UI应该是什么样子,而非如何更新
  • 单向数据流:数据从父组件流向子组件

主要特点:

javascript 复制代码
// 1. JSX语法
const element = <h1>Hello, {name}!</h1>;

// 2. 虚拟DOM
// React维护虚拟DOM树,通过diff算法计算最小变更

// 3. 组件复用
function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

// 4. 单向数据流
function Parent() {
  const [data, setData] = useState('parent data');
  return <Child data={data} />; // 数据向下传递
}

2. 虚拟DOM与Diff算法

虚拟DOM原理:

javascript 复制代码
// 1. 真实DOM(慢)
const div = document.createElement('div');
div.className = 'container';
div.textContent = 'Hello';

// 2. 虚拟DOM(快)- JavaScript对象
const vdom = {
  type: 'div',
  props: {
    className: 'container',
    children: 'Hello'
  }
};

// 3. 渲染过程
JSX → 虚拟DOM → Diff算法 → 最小化更新 → 真实DOM

Diff算法策略:

javascript 复制代码
// 1. 同层比较
// React只比较同一层级的节点,不跨层级比较

// 2. 不同类型元素
// 旧: <div><Child /></div>
// 新: <span><Child /></span>
// 结果: 完全卸载div及其子树,重新创建span

// 3. 相同类型元素
// 旧: <div className="old" />
// 新: <div className="new" />
// 结果: 仅更新className属性

// 4. 列表Diff - key的重要性
// ❌ 没有key - 全部重新渲染
<ul>
  {items.map(item => <li>{item}</li>)}
</ul>

// ✅ 有key - 只更新变化的项
<ul>
  {items.map(item => <li key={item.id}>{item.name}</li>)}
</ul>

// 5. key使用规则
// ❌ 使用index作为key(列表会重排时)
{items.map((item, index) => <li key={index}>{item}</li>)}

// ✅ 使用唯一id
{items.map(item => <li key={item.id}>{item}</li>)}

为什么需要虚拟DOM?

javascript 复制代码
// 直接操作DOM的问题
for (let i = 0; i < 1000; i++) {
  const div = document.createElement('div');
  div.textContent = i;
  container.appendChild(div); // 触发1000次重排
}

// 虚拟DOM方案
const vdoms = [];
for (let i = 0; i < 1000; i++) {
  vdoms.push({ type: 'div', props: { children: i } });
}
// 批量计算差异,一次性更新DOM

3. 函数组件与类组件

函数组件(推荐):

javascript 复制代码
import React, { useState, useEffect } from 'react';

function FunctionComponent(props) {
  const [count, setCount] = useState(0);
  
  useEffect(() => {
    console.log('组件挂载或更新');
    return () => console.log('组件卸载');
  }, [count]);
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>+1</button>
    </div>
  );
}

类组件:

javascript 复制代码
import React, { Component } from 'react';

class ClassComponent extends Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }
  
  componentDidMount() {
    console.log('组件挂载');
  }
  
  componentDidUpdate(prevProps, prevState) {
    console.log('组件更新');
  }
  
  componentWillUnmount() {
    console.log('组件卸载');
  }
  
  handleClick = () => {
    this.setState({ count: this.state.count + 1 });
  }
  
  render() {
    return (
      <div>
        <p>Count: {this.state.count}</p>
        <button onClick={this.handleClick}>+1</button>
      </div>
    );
  }
}

对比:

特性 函数组件 类组件
语法 简洁 复杂
this绑定 无需关心 需要手动绑定
生命周期 Hooks 生命周期方法
性能 更优 稍差
趋势 官方推荐 逐渐淘汰

二、React Hooks详解

4. useState的使用与原理

基本使用:

javascript 复制代码
import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);
  
  // 直接更新
  const increment = () => setCount(count + 1);
  
  // 函数式更新(推荐)
  const increment = () => setCount(prev => prev + 1);
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>+1</button>
    </div>
  );
}

useState原理简化实现:

javascript 复制代码
let state = [];
let setters = [];
let cursor = 0;

function useState(initialValue) {
  const currentCursor = cursor;
  
  state[currentCursor] = state[currentCursor] || initialValue;
  
  const setState = (newValue) => {
    state[currentCursor] = newValue;
    render(); // 触发重新渲染
  };
  
  setters[currentCursor] = setState;
  cursor++;
  
  return [state[currentCursor], setState];
}

function render() {
  cursor = 0; // 重置游标
  ReactDOM.render(<App />, root);
}

高级用法:

javascript 复制代码
// 1. 惰性初始化(初始值计算成本高时使用)
const [state, setState] = useState(() => {
  const initialState = computeExpensiveValue();
  return initialState;
});

// 2. 对象状态更新
const [user, setUser] = useState({ name: '', age: 0 });

// ❌ 错误:直接修改不会触发更新
user.name = 'Alice';

// ✅ 正确:创建新对象
setUser({ ...user, name: 'Alice' });

// 3. 多个相关状态
// ❌ 分散管理
const [name, setName] = useState('');
const [age, setAge] = useState(0);
const [email, setEmail] = useState('');

// ✅ 集中管理
const [user, setUser] = useState({
  name: '',
  age: 0,
  email: ''
});

5. useEffect的使用与原理

基本使用:

javascript 复制代码
import { useEffect } from 'react';

function Component() {
  // 1. 无依赖 - 每次渲染都执行
  useEffect(() => {
    console.log('每次渲染都执行');
  });
  
  // 2. 空依赖 - 仅挂载时执行一次
  useEffect(() => {
    console.log('组件挂载');
    return () => console.log('组件卸载');
  }, []);
  
  // 3. 有依赖 - 依赖变化时执行
  const [count, setCount] = useState(0);
  useEffect(() => {
    console.log('count变化:', count);
  }, [count]);
}

常见场景:

javascript 复制代码
// 1. 数据获取
useEffect(() => {
  async function fetchData() {
    const response = await fetch('/api/users');
    const data = await response.json();
    setUsers(data);
  }
  fetchData();
}, []);

// 2. 订阅事件
useEffect(() => {
  const handleResize = () => {
    setWidth(window.innerWidth);
  };
  
  window.addEventListener('resize', handleResize);
  
  return () => {
    window.removeEventListener('resize', handleResize);
  };
}, []);

// 3. 定时器
useEffect(() => {
  const timer = setInterval(() => {
    setCount(c => c + 1);
  }, 1000);
  
  return () => clearInterval(timer);
}, []);

// 4. 监听props变化
useEffect(() => {
  if (userId) {
    fetchUserData(userId);
  }
}, [userId]);

注意事项:

javascript 复制代码
// ❌ 依赖缺失
const [count, setCount] = useState(0);
useEffect(() => {
  const timer = setInterval(() => {
    setCount(count + 1); // count始终是初始值
  }, 1000);
  return () => clearInterval(timer);
}, []); // 缺少count依赖

// ✅ 正确方式1:添加依赖
useEffect(() => {
  const timer = setInterval(() => {
    setCount(count + 1);
  }, 1000);
  return () => clearInterval(timer);
}, [count]);

// ✅ 正确方式2:函数式更新
useEffect(() => {
  const timer = setInterval(() => {
    setCount(c => c + 1);
  }, 1000);
  return () => clearInterval(timer);
}, []);

6. useContext、useReducer、useMemo、useCallback

useContext - 跨组件传递数据:

javascript 复制代码
import { createContext, useContext } from 'react';

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

// 2. Provider提供数据
function App() {
  const [theme, setTheme] = useState('light');
  
  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      <Toolbar />
    </ThemeContext.Provider>
  );
}

// 3. Consumer使用数据
function ThemedButton() {
  const { theme, setTheme } = useContext(ThemeContext);
  
  return (
    <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
      Current theme: {theme}
    </button>
  );
}

useReducer - 复杂状态管理:

javascript 复制代码
import { useReducer } from 'react';

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

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

// 3. 复杂场景
function todoReducer(state, action) {
  switch (action.type) {
    case 'add':
      return [...state, { id: Date.now(), text: action.text, done: false }];
    case 'toggle':
      return state.map(todo =>
        todo.id === action.id ? { ...todo, done: !todo.done } : todo
      );
    case 'delete':
      return state.filter(todo => todo.id !== action.id);
    default:
      return state;
  }
}

useMemo - 性能优化(计算缓存):

javascript 复制代码
import { useMemo } from 'react';

function ExpensiveComponent({ items, filter }) {
  // ❌ 每次渲染都计算
  const filteredItems = items.filter(item => item.includes(filter));
  
  // ✅ 仅在依赖变化时计算
  const filteredItems = useMemo(() => {
    console.log('重新计算');
    return items.filter(item => item.includes(filter));
  }, [items, filter]);
  
  return <List items={filteredItems} />;
}

// 使用场景
// 1. 复杂计算
const expensiveValue = useMemo(() => {
  return computeExpensiveValue(a, b);
}, [a, b]);

// 2. 引用相等性
const config = useMemo(() => ({ api: '/api', timeout: 5000 }), []);

useCallback - 性能优化(函数缓存):

javascript 复制代码
import { useCallback } from 'react';

function Parent() {
  const [count, setCount] = useState(0);
  
  // ❌ 每次渲染创建新函数,导致子组件重新渲染
  const handleClick = () => {
    console.log('clicked');
  };
  
  // ✅ 缓存函数引用
  const handleClick = useCallback(() => {
    console.log('clicked');
  }, []);
  
  // 依赖count
  const increment = useCallback(() => {
    setCount(c => c + 1);
  }, []);
  
  return <Child onClick={handleClick} />;
}

// 子组件使用React.memo优化
const Child = React.memo(({ onClick }) => {
  console.log('Child渲染');
  return <button onClick={onClick}>Click</button>;
});

useCallback vs useMemo:

javascript 复制代码
// useCallback缓存函数
const memoizedCallback = useCallback(() => {
  doSomething(a, b);
}, [a, b]);

// 等价于
const memoizedCallback = useMemo(() => {
  return () => doSomething(a, b);
}, [a, b]);

7. 自定义Hooks

基本规则:

  • 以"use"开头命名
  • 只在函数组件或其他Hooks中调用
  • 遵循Hooks规则(不在循环、条件、嵌套函数中调用)
javascript 复制代码
// 1. useLocalStorage - 同步localStorage
function useLocalStorage(key, initialValue) {
  const [value, setValue] = useState(() => {
    const item = localStorage.getItem(key);
    return item ? JSON.parse(item) : initialValue;
  });
  
  useEffect(() => {
    localStorage.setItem(key, JSON.stringify(value));
  }, [key, value]);
  
  return [value, setValue];
}

// 使用
function App() {
  const [name, setName] = useLocalStorage('name', 'Guest');
  return <input value={name} onChange={e => setName(e.target.value)} />;
}

// 2. useDebounce - 防抖
function useDebounce(value, delay) {
  const [debouncedValue, setDebouncedValue] = useState(value);
  
  useEffect(() => {
    const timer = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);
    
    return () => clearTimeout(timer);
  }, [value, delay]);
  
  return debouncedValue;
}

// 使用
function SearchComponent() {
  const [searchTerm, setSearchTerm] = useState('');
  const debouncedSearchTerm = useDebounce(searchTerm, 500);
  
  useEffect(() => {
    if (debouncedSearchTerm) {
      // 执行搜索
      fetchSearchResults(debouncedSearchTerm);
    }
  }, [debouncedSearchTerm]);
  
  return <input value={searchTerm} onChange={e => setSearchTerm(e.target.value)} />;
}

// 3. useFetch - 数据获取
function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  
  useEffect(() => {
    let isCancelled = false;
    
    async function fetchData() {
      try {
        setLoading(true);
        const response = await fetch(url);
        const json = await response.json();
        
        if (!isCancelled) {
          setData(json);
          setError(null);
        }
      } catch (err) {
        if (!isCancelled) {
          setError(err);
        }
      } finally {
        if (!isCancelled) {
          setLoading(false);
        }
      }
    }
    
    fetchData();
    
    return () => {
      isCancelled = true;
    };
  }, [url]);
  
  return { data, loading, error };
}

// 使用
function UserList() {
  const { data: users, loading, error } = useFetch('/api/users');
  
  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;
  
  return (
    <ul>
      {users.map(user => <li key={user.id}>{user.name}</li>)}
    </ul>
  );
}

// 4. useWindowSize - 监听窗口大小
function useWindowSize() {
  const [size, setSize] = useState({
    width: window.innerWidth,
    height: window.innerHeight
  });
  
  useEffect(() => {
    const handleResize = () => {
      setSize({
        width: window.innerWidth,
        height: window.innerHeight
      });
    };
    
    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);
  
  return size;
}

// 5. usePrevious - 获取上一次的值
function usePrevious(value) {
  const ref = useRef();
  
  useEffect(() => {
    ref.current = value;
  }, [value]);
  
  return ref.current;
}

三、React性能优化

8. React.memo、PureComponent、shouldComponentUpdate

React.memo - 函数组件优化:

javascript 复制代码
// 1. 基本使用
const Child = React.memo(({ name }) => {
  console.log('Child渲染');
  return <div>{name}</div>;
});

// 父组件更新时,如果props未变化,Child不会重新渲染

// 2. 自定义比较函数
const Child = React.memo(
  ({ user }) => {
    return <div>{user.name}</div>;
  },
  (prevProps, nextProps) => {
    // 返回true表示不更新,false表示更新
    return prevProps.user.id === nextProps.user.id;
  }
);

PureComponent - 类组件优化:

javascript 复制代码
import React, { PureComponent } from 'react';

class Child extends PureComponent {
  render() {
    console.log('Child渲染');
    return <div>{this.props.name}</div>;
  }
}

// PureComponent自动实现shouldComponentUpdate,进行浅比较

shouldComponentUpdate - 手动控制更新:

javascript 复制代码
class Child extends React.Component {
  shouldComponentUpdate(nextProps, nextState) {
    // 返回false阻止更新
    if (this.props.name === nextProps.name) {
      return false;
    }
    return true;
  }
  
  render() {
    return <div>{this.props.name}</div>;
  }
}

注意事项:

javascript 复制代码
// ❌ 浅比较陷阱
function Parent() {
  const [count, setCount] = useState(0);
  
  // 每次都是新对象,导致Child总是重新渲染
  const user = { name: 'Alice' };
  
  return <Child user={user} />;
}

// ✅ 使用useMemo
function Parent() {
  const [count, setCount] = useState(0);
  
  const user = useMemo(() => ({ name: 'Alice' }), []);
  
  return <Child user={user} />;
}

9. 代码分割与懒加载

javascript 复制代码
import React, { lazy, Suspense } from 'react';

// 1. 路由懒加载
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));

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

// 2. 条件懒加载
function AdminPanel() {
  const [showAdmin, setShowAdmin] = useState(false);
  const AdminComponent = lazy(() => import('./AdminComponent'));
  
  return (
    <div>
      <button onClick={() => setShowAdmin(true)}>Show Admin</button>
      {showAdmin && (
        <Suspense fallback={<div>Loading admin...</div>}>
          <AdminComponent />
        </Suspense>
      )}
    </div>
  );
}

// 3. 错误边界
class ErrorBoundary extends React.Component {
  state = { hasError: false };
  
  static getDerivedStateFromError(error) {
    return { hasError: true };
  }
  
  componentDidCatch(error, info) {
    console.error('Error:', error, info);
  }
  
  render() {
    if (this.state.hasError) {
      return <h1>Something went wrong.</h1>;
    }
    return this.props.children;
  }
}

// 使用
<ErrorBoundary>
  <Suspense fallback={<div>Loading...</div>}>
    <LazyComponent />
  </Suspense>
</ErrorBoundary>

10. 列表渲染优化

javascript 复制代码
// 1. 虚拟列表(react-window)
import { FixedSizeList } from 'react-window';

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

// 2. 分页加载
function PaginatedList() {
  const [page, setPage] = useState(1);
  const { data } = useFetch(`/api/items?page=${page}`);
  
  return (
    <div>
      <List items={data} />
      <button onClick={() => setPage(p => p + 1)}>Load More</button>
    </div>
  );
}

// 3. 无限滚动
function InfiniteScroll() {
  const [items, setItems] = useState([]);
  const [page, setPage] = useState(1);
  const observerRef = useRef();
  
  useEffect(() => {
    fetchItems(page).then(newItems => {
      setItems(prev => [...prev, ...newItems]);
    });
  }, [page]);
  
  useEffect(() => {
    const observer = new IntersectionObserver(
      entries => {
        if (entries[0].isIntersecting) {
          setPage(p => p + 1);
        }
      },
      { threshold: 1.0 }
    );
    
    if (observerRef.current) {
      observer.observe(observerRef.current);
    }
    
    return () => observer.disconnect();
  }, []);
  
  return (
    <div>
      {items.map(item => <div key={item.id}>{item.name}</div>)}
      <div ref={observerRef}>Loading...</div>
    </div>
  );
}
相关推荐
ttwuai4 小时前
XYGo Admin 国际化实战:Vue3 中后台多语言方案详解
前端·javascript·vue.js·vue
IT_陈寒4 小时前
React状态更新后视图不刷新?我差点以为是灵异事件
前端·人工智能·后端
Csvn4 小时前
JS 技巧:设计模式(下)- 策略、装饰器、代理
前端
一颗小青松4 小时前
uniapp 集成友盟并且上传页面路径
前端·vue.js·uni-app
周淳APP4 小时前
微前端核心沙箱机制深度解析:从iframe到乾坤沙箱
前端·学习·iframe·微前端·qiankun·前端架构
JarvanMo4 小时前
Android View 相关工具包终于成为了历史
前端
2501_940041744 小时前
应用构建:前端复杂交互与数据可视化的进阶之路
前端·信息可视化
前端若水4 小时前
项目初始化:Vite + React + shadcn/ui
前端·react.js·ui
ZC跨境爬虫4 小时前
模块化烹饪小程序开发日记 Day4:网络层基础设施与接口治理实践
前端·javascript·数据库·ui·html