十二月 21, 2025
31 分钟阅读

初级前端开发工程师面试题:React、工具和进阶主题

interview
career-advice
job-search
entry-level
初级前端开发工程师面试题:React、工具和进阶主题
MB

Milad Bonakdar

作者

通过 23 个必备面试题,掌握 React、构建工具、性能优化、调试、可访问性、测试和软技能。为 2024-2025 年的初级前端开发工程师面试做好充分准备。


前言

本综合指南包含 21 个精心挑选的面试问题,涵盖高级前端开发主题:React 和框架、构建工具和版本控制、性能优化、调试、可访问性、测试和软技能。这些是初级前端开发人员在 2024-2025 年面试中实际遇到的问题。每个问题都包含详尽的答案、稀有度评估和难度评级,这些评级基于对来自主要科技公司和初创公司的数百次真实面试的分析。

这是我们完整面试指南的第二部分。有关 HTML、CSS 和 JavaScript 的基础知识,请查看第一部分:HTML、CSS 和 JavaScript 基础知识


React & 框架 (8 个问题)

33. 什么是 JSX,为什么我们在 React 中使用它?

回答: JSX(JavaScript XML)是 JavaScript 的一种语法扩展,它看起来类似于 HTML,用于描述 React 中的 UI 结构。

示例:

const element = <h1 className="title">Hello, {name}!</h1>;

// 编译后:
const element = React.createElement(
  'h1',
  { className: 'title' },
  'Hello, ',
  name,
  '!'
);

优点:

  • React.createElement() 更具可读性和直观性
  • 看起来像 HTML,但具有完整的 JavaScript 功能
  • 支持花括号内的表达式 {}
  • 使用 TypeScript 时类型安全
  • 对开发者来说是熟悉的语法

与 HTML 的主要区别:

  • 使用 className 而不是 class
  • 使用 htmlFor 而不是 for
  • 所有属性都使用驼峰命名法 (onClick, onChange)
  • 必须关闭所有标签(包括 <img />, <br />

稀有度: 常见
难度: 简单


34. 解释 React 中 props 和 state 的区别

回答:

Props(属性):

  • 数据从父组件传递到子组件
  • 只读(从子组件的角度来看是不可变的)
  • 用于组件配置
  • 更改来自父组件的重新渲染
// 父组件
function Parent() {
  return <Child name="Alice" age={25} />;
}

// 子组件
function Child({ name, age }) {
  return <p>{name} is {age} years old</p>;
  // 无法修改 name 或 age
}

State(状态):

  • 组件内部管理的数据
  • 可变(可以使用 setter 函数更改)
  • 更改会触发重新渲染
  • 组件私有
function Counter() {
  const [count, setCount] = useState(0);
  
  return (
    <button onClick={() => setCount(count + 1)}>
      Count: {count}
    </button>
  );
}

主要区别: Props 向下流动(父组件到子组件),state 是组件本地的。

稀有度: 常见
难度: 简单


35. 什么是 useState hook,你如何使用它?

回答: useState 是一个 React Hook,它为函数组件添加了状态管理。

语法:

const [stateVariable, setStateFunction] = useState(initialValue);

示例:

// 简单的计数器
function Counter() {
  const [count, setCount] = useState(0);
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <button onClick={() => setCount(0)}>Reset</button>
    </div>
  );
}

// 对象状态
function Form() {
  const [user, setUser] = useState({ name: '', email: '' });
  
  const handleChange = (e) => {
    setUser({
      ...user,
      [e.target.name]: e.target.value
    });
  };
  
  return (
    <input name="name" value={user.name} onChange={handleChange} />
  );
}

// 延迟初始化(昂贵的计算)
const [data, setData] = useState(() => {
  return expensiveComputation();
});

要点:

  • 状态更新会触发重新渲染
  • 状态更新是异步的
  • 对基于先前状态的状态使用函数式更新:setCount(prev => prev + 1)

稀有度: 常见
难度: 简单-中等


36. useEffect hook 做什么?解释依赖数组。

回答: useEffect 在渲染后运行副作用(数据获取、订阅、DOM 操作)。

语法:

useEffect(() => {
  // 副作用代码在这里
  
  return () => {
    // 清理(可选)
  };
}, [dependencies]);

依赖数组行为:

// 1. 没有依赖数组 - 每次渲染后运行
useEffect(() => {
  console.log('Runs every render');
});

// 2. 空数组 [] - 仅在挂载时运行一次
useEffect(() => {
  console.log('Runs only on mount');
  fetchData();
}, []);

// 3. 带有依赖项 - 当依赖项更改时运行
useEffect(() => {
  console.log('Runs when count changes');
  document.title = `Count: ${count}`;
}, [count]);

常见用例:

// 获取数据
useEffect(() => {
  fetch('/api/users')
    .then(res => res.json())
    .then(data => setUsers(data));
}, []);

// 事件监听器(带有清理)
useEffect(() => {
  const handleResize = () => setWidth(window.innerWidth);
  window.addEventListener('resize', handleResize);
  
  return () => window.removeEventListener('resize', handleResize);
}, []);

稀有度: 常见
难度: 中等


37. 你如何在 React 中渲染一个项目列表?为什么我们需要 keys?

回答:

渲染列表:

function TodoList({ todos }) {
  return (
    <ul>
      {todos.map((todo) => (
        <li key={todo.id}>
          {todo.text}
        </li>
      ))}
    </ul>
  );
}

为什么 keys 很重要: Keys 帮助 React 识别哪些项目已更改、已添加或已删除。它们通过以下方式实现高效更新:

  • 最小化 DOM 操作
  • 保留组件状态
  • 维护正确的元素标识
  • 优化协调算法

Key 指南:

// 好 - 唯一的、稳定的 ID
items.map(item => <Item key={item.id} {...item} />)

// 坏 - 数组索引(列表更改时不稳定)
items.map((item, index) => <Item key={index} {...item} />)

// 坏 - 非唯一的 keys 会导致错误
items.map(item => <Item key={item.category} {...item} />)

什么时候可以接受索引: 永远不会重新排序或更改的静态列表。

稀有度: 常见
难度: 简单-中等


38. 什么是 Virtual DOM,为什么 React 使用它?

回答: Virtual DOM 是实际 DOM 的轻量级 JavaScript 表示,React 在内存中维护它。

它是如何工作的:

  1. 渲染: 当状态改变时,React 创建 Virtual DOM 树
  2. Diff: React 将新的 Virtual DOM 与之前的版本进行比较(协调)
  3. 更新: React 计算所需的最小更改
  4. Patch: React 仅更新真实 DOM 中已更改的部分

为什么它是有益的:

  • 性能: 直接 DOM 操作很慢;React 批量处理并最小化更新
  • 效率: 仅更新已更改的元素,而不是整个树
  • 开发者体验: 编写声明式代码,React 处理高效更新
  • 抽象: 相同的代码可以针对不同的平台(React Native、VR)

示例:

// 你编写:
<div>{count}</div>

// 当 count 从 0 变为 1 时:
// React 仅更新文本节点,而不是整个 div

注意: 现代 React(Fiber 架构)实际上并没有区分两个 Virtual DOM,而是使用与 Fiber 节点类似的概念。

稀有度: 常见
难度: 中等


39. 你如何在 React 中处理表单?

回答:

受控组件(推荐): 表单元素值由 React 状态控制。

function LoginForm() {
  const [formData, setFormData] = useState({
    email: '',
    password: ''
  });
  
  const handleChange = (e) => {
    const { name, value } = e.target;
    setFormData(prev => ({
      ...prev,
      [name]: value
    }));
  };
  
  const handleSubmit = (e) => {
    e.preventDefault();  // 阻止页面重新加载
    console.log('Submitted:', formData);
    // 在这里进行 API 调用
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <input
        name="email"
        value={formData.email}
        onChange={handleChange}
        type="email"
      />
      <input
        name="password"
        value={formData.password}
        onChange={handleChange}
        type="password"
      />
      <button type="submit">Login</button>
    </form>
  );
}

非受控组件(不太常见): 使用 refs 直接访问 DOM 值。

function UncontrolledForm() {
  const emailRef = useRef();
  
  const handleSubmit = (e) => {
    e.preventDefault();
    console.log(emailRef.current.value);
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <input ref={emailRef} type="email" />
    </form>
  );
}

最佳实践: 在大多数情况下使用受控组件。

稀有度: 常见
难度: 中等


40. 函数组件和类组件有什么区别?

回答:

函数组件(现代,首选):

function Welcome({ name }) {
  const [count, setCount] = useState(0);
  
  useEffect(() => {
    // 副作用
  }, []);
  
  return <h1>Hello, {name}!</h1>;
}

类组件(旧版):

class Welcome extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }
  
  componentDidMount() {
    // 副作用
  }
  
  render() {
    return <h1>Hello, {this.props.name}!</h1>;
  }
}

主要区别:

  • 语法: 函数 vs 类
  • 状态: Hooks (useState) vs this.state
  • 生命周期: Hooks (useEffect) vs 生命周期方法
  • this 关键字: 在函数组件中不需要
  • 样板代码: 函数组件中的代码更少
  • 性能: 函数组件的性能略好

现代标准: 带有 Hooks 的函数组件现在是推荐的方法(自 React 16.8 起)。类组件仍然有效,但被认为是旧版。

稀有度: 常见
难度: 简单-中等


构建工具 & 版本控制 (5 个问题)

41. 什么是 npm,package.json 用于什么?

回答: npm(Node Package Manager)是 JavaScript 的默认包管理器,用于安装、管理和共享代码包。

package.json 是清单文件,其中包含:

  • 元数据: 项目名称、版本、描述、作者
  • 依赖项: 生产环境所需的包 (dependencies)
  • 开发依赖项: 仅在开发环境中所需的包 (devDependencies)
  • 脚本: 用于常见任务的命令(start、build、test)

package.json 示例:

{
  "name": "my-app",
  "version": "1.0.0",
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "jest"
  },
  "dependencies": {
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  },
  "devDependencies": {
    "eslint": "^8.0.0"
  }
}

常用命令:

  • npm install - 安装所有依赖项
  • npm install package-name - 安装特定包
  • npm install --save-dev package-name - 作为开发依赖项安装
  • npm run script-name - 运行 npm 脚本
  • npm update - 更新包

稀有度: 常见
难度: 简单


42. 什么是 Git,为什么团队使用它?

回答: Git 是一个分布式版本控制系统,用于跟踪代码随时间的变化。

为什么团队使用 Git:

  • 协作: 多个开发人员在同一代码库上工作而不会发生冲突
  • 历史记录: 所有更改的完整记录(谁、什么、何时、为什么)
  • 分支: 独立地处理功能而不影响主代码
  • 备份: 代码存储在多个位置(本地 + 远程)
  • 回滚: 轻松恢复到以前的工作版本
  • 代码审查: 在合并之前审查更改(pull requests)
  • 实验: 在分支中尝试新想法而没有风险

基本概念:

  • 存储库 (repo): Git 跟踪的项目文件夹
  • 提交 (Commit): 带有消息的更改快照
  • 分支 (Branch): 独立的开发线
  • 合并 (Merge): 合并来自不同分支的更改
  • 远程 (Remote): 在线托管的存储库(GitHub、GitLab)

行业标准: 全球 93% 以上的开发人员使用 Git。

稀有度: 常见
难度: 简单


43. 解释常见的 Git 命令和工作流程

回答:

基本工作流程:

# 克隆存储库
git clone https://github.com/user/repo.git

# 检查状态
git status

# 创建新分支
git checkout -b feature-name
# 或现代语法:
git switch -c feature-name

# 进行更改,然后暂存
git add file.js
git add .  # 暂存所有更改

# 提交并附带消息
git commit -m "添加登录功能"

# 推送到远程
git push origin feature-name

# 拉取最新更改
git pull origin main

# 合并分支
git checkout main
git merge feature-name

# 查看历史记录
git log

初级人员必须知道的基本命令:

  • clone - 复制远程存储库
  • add - 暂存更改
  • commit - 保存带有消息的快照
  • push - 上传到远程
  • pull - 下载 + 合并远程更改
  • branch - 列出/创建分支
  • checkout/switch - 切换分支
  • status - 查看当前状态
  • log - 查看提交历史

稀有度: 常见
难度: 简单-中等


44. 什么是合并冲突,你将如何解决它?

回答: 当 Git 无法自动合并更改时,会发生合并冲突,因为同一行代码上存在相互竞争的修改。

常见场景:

# 开发者 A 更改了第 5 行
# 开发者 B 也更改了第 5 行
# 合并时,Git 不知道要保留哪个

冲突标记:

<<<<<<< HEAD
const greeting = "Hello";
=======
const greeting = "Hi";
>>>>>>> feature-branch

解决方法:

  1. 识别冲突: Git 标记带有冲突的文件
  2. 打开冲突文件: 查找冲突标记
  3. 决定保留什么: 审查两个更改
  4. 编辑代码: 删除标记,保留所需的代码
  5. 测试: 确保代码正常工作
  6. 暂存已解决的文件: git add file.js
  7. 完成合并: git commit

最佳实践:

  • 与团队沟通
  • 经常拉取以最大程度地减少冲突
  • 保持提交小而集中
  • 解决后彻底测试

稀有度: 常见
难度: 中等


45. Webpack 做什么?什么是 Vite?

回答:

Webpack: 一个模块打包器,它接受 JavaScript、CSS、图像和其他资产,处理它们,并将它们打包成针对浏览器优化的文件。

它做什么:

  • 将多个文件捆绑到更少的文件中
  • 转换代码(Babel 用于 JSX/ES6,Sass 到 CSS)
  • 为生产环境进行优化(压缩、tree shaking)
  • 处理依赖关系
  • 代码拆分以提高性能

基本概念:

src/
  index.js
  component.js
  styles.css
          ↓ Webpack
dist/
  bundle.js (所有组合和优化)

Vite: 现代构建工具,比 Webpack 等传统打包器快得多。

为什么 Vite 更快:

  • 在开发期间使用原生 ES 模块(无需打包)
  • 热模块更换 (HMR) 是即时的
  • 仅为生产环境打包
  • 更好的开发者体验

什么时候使用:

  • Vite: 新项目、现代框架(React、Vue)
  • Webpack: 现有项目,需要复杂的配置

稀有度: 常见
难度: 简单-中等


Web 性能 & 优化 (3 个问题)

46. 你将如何优化网站的加载性能?

回答:

图像优化:

  • 压缩图像(照片使用 JPG,图形使用 PNG,两者都使用 WebP)
  • 使用适当的大小(不要为 300 像素显示加载 4000 像素图像)
  • 延迟加载首屏下方的图像
  • 使用 srcset 获取响应式图像

代码优化:

  • 缩小 JavaScript、CSS、HTML(删除空格、注释)
  • 捆绑和压缩文件(gzip 或 Brotli)
  • 删除未使用的 CSS/JS (tree shaking)
  • 代码拆分(每页仅加载所需的代码)

缓存:

  • 具有适当缓存标头的浏览器缓存
  • 使用 CDN 获取静态资产
  • 用于离线功能的服务工作线程

加载策略:

  • <head> 中的关键 CSS 内联
  • 延迟非关键 JavaScript
  • 预加载重要资源
  • 减少 HTTP 请求

性能指标:

  • 使用 Lighthouse、PageSpeed Insights 进行测量
  • 目标:FCP < 1.8 秒,LCP < 2.5 秒,CLS < 0.1

稀有度: 常见
难度: 中等


47. 你会使用哪些工具来衡量网站性能?

回答:

浏览器开发者工具:

  • Chrome DevTools Network 选项卡: 分析请求计时、文件大小、加载顺序
  • Performance/Lighthouse 选项卡: 生成带有分数的性能报告
  • Coverage 选项卡: 查找未使用的 JavaScript/CSS
  • Console: 使用 console.time() 和 Performance API 进行测量

在线工具:

  • Google PageSpeed Insights: 获取性能分数和建议
  • WebPageTest: 详细的瀑布图,多个位置
  • GTmetrix: 带有等级的性能分析

要监控的指标:

  • First Contentful Paint (FCP): 何时出现第一个内容
  • Largest Contentful Paint (LCP): 何时加载主要内容
  • Time to Interactive (TTI): 页面何时变为交互式
  • Cumulative Layout Shift (CLS): 视觉稳定性
  • Total Blocking Time (TBT): 主线程阻塞时间

对于初级人员: 熟悉 Chrome DevTools 和 Lighthouse 的基本知识就足够了。不期望有深入的分析技能。

稀有度: 常见
难度: 简单-中等


48. 什么是延迟加载,你什么时候会使用它?

回答: 延迟加载会延迟加载非关键资源,直到需要它们为止,通常是当它们即将进入视口时。

常见用例:

图像:

<img src="placeholder.jpg" data-src="actual-image.jpg" loading="lazy" alt="Description">

原生延迟加载(现代浏览器):

<img src="image.jpg" loading="lazy" alt="Description">

JavaScript/React:

// 组件延迟加载
const HeavyComponent = React.lazy(() => import('./HeavyComponent'));

function App() {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <HeavyComponent />
    </Suspense>
  );
}

优点:

  • 更快的初始页面加载
  • 减少带宽使用
  • 在慢速连接上获得更好的性能
  • 改善用户体验(内容显示得更快)

什么时候使用:

  • 首屏下方的图像
  • 不是立即可见的繁重组件
  • 选项卡/手风琴中的内容
  • 无限滚动实现

稀有度: 不常见
难度: 中等


调试 & 开发者工具 (2 个问题)

49. 你如何在浏览器中调试 JavaScript 错误?

回答:

逐步调试过程:

1. 检查控制台:

console.log(variable);      // 检查值
console.error('Error:', error);  // 记录错误
console.table(arrayOfObjects);   // 在表中查看数据
console.warn('Warning');    // 突出显示警告

2. 使用断点:

  • 打开 DevTools Sources/Debugger 面板
  • 单击行号以设置断点
  • 代码执行在断点处暂停
  • 在 Scope 面板中检查变量
  • 单步执行代码(Step Over、Step Into、Step Out)

3. 检查堆栈跟踪:

  • 错误消息显示文件名和行号
  • 按照调用堆栈查找错误来源
  • 检查 Network 选项卡中的网络错误

4. 调试 DOM 问题:

  • 在 Elements 面板中检查元素
  • 检查计算样式
  • 使用 :hover 状态强制
  • 验证附加的事件侦听器

5. 检查网络请求:

  • Network 选项卡显示失败的 API 调用
  • 检查请求/响应标头
  • 检查状态代码(404、500 等)

常见调试技术:

  • 添加 debugger; 语句以暂停执行
  • 使用条件断点
  • 监视表达式
  • 黑盒第三方代码

稀有度: 常见
难度: 简单-中等


50. Chrome DevTools 中的主要面板是什么,你用它们做什么?

回答:

基本面板:

1. 控制台:

  • 查看 JavaScript 错误和日志
  • 执行 JavaScript 命令
  • 以交互方式测试表达式
  • API 测试

2. 元素(检查器):

  • 检查和修改 HTML 结构
  • 实时编辑 CSS
  • 调试布局问题
  • 查看计算样式、框模型
  • 测试响应式设计

3. 来源(调试器):

  • 查看源文件
  • 设置断点
  • 单步执行代码执行
  • 编辑和保存更改(工作区)
  • 查看调用堆栈

4. 网络:

  • 监控 HTTP 请求/响应
  • 检查加载时间和文件大小
  • 调试 API 调用
  • 按类型过滤(JS、CSS、XHR、图像)
  • 限制网络速度

5. 性能/Lighthouse:

  • 分析运行时性能
  • 生成性能审核
  • 识别瓶颈
  • 检查 Core Web Vitals

6. 应用程序:

  • 检查本地存储、会话存储
  • 查看和清除 cookie
  • 检查服务工作线程
  • 管理缓存

对于初级人员: 预计要非常熟悉 Console、Elements、Sources 和 Network。性能分析是一个加分项。

稀有度: 常见
难度: 简单


可访问性 & 最佳实践 (2 个问题)

51. 什么是 Web 可访问性,为什么它很重要?

回答: Web 可访问性是指设计和开发每个人都可以使用的网站,包括残疾人(视觉、听觉、运动、认知)。

为什么它很重要:

1. 包容性设计: 世界上 15% 的人口患有某种形式的残疾 2. 法律要求: ADA(美国残疾人法案)、第 508 条、WCAG 合规性 3. 对所有人都更好的 UX: 字幕有助于嘈杂的环境,键盘导航有助于高级用户 4. SEO 优势: 语义 HTML 和适当的结构可提高搜索排名 5. 更大的受众: 不要排除潜在的用户/客户 6. 道德责任: 平等访问是一项人权

要考虑的常见残疾:

  • 视觉:失明、低视力、色盲
  • 听觉:耳聋、听力损失
  • 运动:行动不便、震颤、瘫痪
  • 认知:学习障碍、记忆问题

商业案例: 可访问的网站可以覆盖更多的用户,在搜索中排名更高,避免诉讼,并展示企业责任。

稀有度: 常见
难度: 简单-中等


52. 使网站更易于访问的一些基本方法是什么?

回答:

HTML 结构:

  • 使用语义元素(<header>, <nav>, <main>, <article>, <footer>
  • 正确的标题层次结构(H1 → H2 → H3,不要跳过)
  • 正确标记表单输入
<label for="email">Email:</label>
<input id="email" type="email" name="email" />

图像:

  • 始终包含描述性 alt 文本
  • 对装饰性图像使用空 alt:alt=""

键盘导航:

  • 所有交互元素都应该是键盘可访问的
  • 逻辑选项卡顺序
  • 可见焦点指示器
  • 不要删除 :focus 轮廓

颜色和对比度:

  • 足够的颜色对比度(普通文本为 4.5:1,大文本为 3:1)
  • 不要仅仅依靠颜色来传达信息
  • 使用色盲模拟器进行测试

ARIA 属性(在需要时):

<button aria-label="Close dialog">×</button>
<div role="alert" aria-live="polite">Changes saved</div>
<nav aria-label="Main navigation">...</nav>

测试:

  • 使用屏幕阅读器(VoiceOver、NVDA、JAWS)
  • 仅键盘导航
  • 浏览器可访问性工具(Lighthouse、axe)

稀有度: 常见
难度: 中等


测试意识 (1 个问题)

53. 什么是单元测试,为什么它很重要?

回答: 单元测试涉及隔离测试单个函数或组件,以验证它们是否正常工作。

它测试什么:

  • 函数是否为给定的输入返回预期的输出?
  • 组件是否使用给定的 props 正确渲染?
  • 边缘情况是否正常工作?

示例 (Jest):

// 要测试的函数
function add(a, b) {
  return a + b;
}

// 单元测试
test('adds 1 + 2 to equal 3', () => {
  expect(add(1, 2)).toBe(3);
});

test('handles negative numbers', () => {
  expect(add(-1, -2)).toBe(-3);
});

为什么它很重要:

  • 尽早发现错误: 在生产之前发现问题
  • 更改的信心: 无需担心即可重构
  • 文档: 测试显示代码应该如何使用
  • 更快的调试: 查明确切的故障位置
  • 更好的设计: 可测试的代码通常是更清晰的代码

测试金字塔:

  • 许多单元测试(快速、便宜)
  • 更少的集成测试(一起测试组件)
  • 很少的端到端测试(完整的用户流程,缓慢)

对于初级人员: 了解测试为何重要以及基本概念就足够了。通常不期望为入门级职位编写全面的测试套件,但学习测试的意愿很重要。

常用框架: Jest、React Testing Library、Mocha、Jasmine

稀有度: 常见
难度: 简单-中等


总计:21 个问题

此集合代表初级前端开发人员在面试中遇到的高级主题和工具。结合第一部分:HTML、CSS 和 JavaScript 基础知识,你将完整覆盖 2024-2025 年的 55 个基本面试问题。

Newsletter subscription

真正有效的每周职业建议

将最新见解直接发送到您的收件箱

Decorative doodle

创建一份让您被录用速度提高60%的简历

在几分钟内,创建一份量身定制的、ATS友好的简历,已证明可以获得6倍以上的面试机会。

创建更好的简历

分享这篇文章

战胜75%的ATS拒绝率

4份简历中有3份从未被人眼看到。我们的关键词优化将您的通过率提高了80%,确保招聘人员真正看到您的潜力。