首页/文章/八股文

面试中被问到过的前端八股

2025-09-15
2906 字约 10 分钟
...

1、React的事件和普通事件有什么不同

✅ 1. 事件绑定方式不同

React 中使用的是 驼峰命名,并且直接传入函数引用:

<button onClick={handleClick}>点我</button>

原生 DOM 则是小写并用字符串或函数赋值:

<button onclick="handleClick()">点我</button>

或者在 JS 中绑定:

element.addEventListener('click', handleClick)

✅ 2. React 使用的是合成事件(SyntheticEvent)

React 实现了一套自己的事件系统:SyntheticEvent 合成事件。

  • 它对原生事件进行了封装,兼容性更强。
  • 所有事件都会被委托到最外层(document),提升性能(事件委托机制)。
  • e 是一个合成事件对象,不是原生事件,但可以通过 e.nativeEvent 访问原生事件。
function handleClick(e) {
  console.log(e); 
  console.log(e.nativeEvent); 
}

✅ 3. 阻止默认行为和冒泡方式不同

React 的合成事件里不能直接使用 return false,而是要使用专门方法:

function handleClick(e) {
  e.preventDefault(); 
  e.stopPropagation(); 
}

✅ 4. 事件解绑方式不同

React 中不推荐在组件内部动态绑定/解绑事件,因为 React 会在重新渲染时自动处理。

如果真要控制事件生命周期,推荐使用 useEffect 中手动绑定原生事件:

useEffect(() => {
  const handler = () => console.log('clicked');
  window.addEventListener('click', handler);
  return () => window.removeEventListener('click', handler);
}, []);

2、React的事件委托机制

🧠 什么是事件委托?

事件委托的意思是:不在每个子元素上绑定事件,而是把事件绑定到父元素,利用事件冒泡机制来处理子元素的事件。

🧩 React 是怎么做事件委托的?

React 并不是在每一个 DOM 节点上都添加事件监听器,而是:

✅ 把所有事件都绑定在根节点(通常是 document 或 root 容器)上。 然后:

  1. 当事件发生时,比如你点击了一个按钮;

  2. 浏览器的事件冒泡机制会把事件从按钮冒泡到 document;

  3. React 统一在顶层监听到这个事件后,会:

    • 根据事件目标(event.target)和虚拟 DOM 中注册的事件,
    • 找到对应的组件方法,调用你写的事件处理函数。

🧪 举个例子
function App() {
  function handleClick() {
    console.log('按钮被点击了!');
  }
​
  return <button onClick={handleClick}>点我</button>;
}

你写了一个 onClick,React 并不是在这个 button 上真的加了一个 click 事件,而是:

  • 把事件注册到了 document 上;
  • 通过事件冒泡捕获到点击;
  • 再根据虚拟 DOM 的结构找到这个按钮的 onClick 函数并执行。

🌟 为什么这么做?

好处是:

  • ✅ 减少真实 DOM 上的事件绑定数量;
  • ✅ 更容易做统一管理(比如事件池、清理、合成事件等);
  • ✅ 提升性能,尤其是组件多、结构复杂的情况下。

🧬 React 的合成事件(SyntheticEvent)

React 实际上不是直接处理原生事件,而是封装了一层叫做 合成事件(SyntheticEvent) 的机制:

  • 它是对原生事件的包装;
  • 提供跨浏览器一致的事件属性和行为;
  • 同时还能复用事件对象(提升性能);
  • 最终仍然会在事件执行结束后调用原生事件。

🧯 注意点

  • React 的事件是 冒泡阶段触发 的;
  • 如果你使用了 stopPropagation(),事件就不会继续冒泡;
  • 如果你使用 nativeEvent,那是原生事件对象(需要小心使用);

3、React中如何避免不必要的render

🧠 1. 使用 React.memo(函数组件)

对函数组件来说,React.memo 是最常用的优化方式。

const MyComponent = React.memo((props) => {
  console.log("render");
  return <div>{props.name}</div>;
});
  • 默认情况下,React.memo 会浅比较 props,如果 props 没变化,就跳过渲染。
  • 如果你需要自定义比较逻辑,可以传第二个参数:
React.memo(Component, (prevProps, nextProps) => {
  return prevProps.id === nextProps.id; // 返回 true 表示“跳过更新”
});

🧠 2. 使用 useMemo 缓存计算结果

当你有复杂计算或生成的变量,用 useMemo 可以避免每次 render 都重新计算:

const expensiveValue = useMemo(() => {
  return computeExpensiveValue(a, b)
}, [a, b])
  • 只有当依赖项变了才会重新计算。

🧠 3. 使用 useCallback 缓存函数引用

传函数作为 prop 时,如果每次都传一个新函数,会让子组件以为 props 变了导致重渲。

const handleClick = useCallback(() => {
  doSomething()
}, [dependency])

搭配 React.memo 使用效果最佳。

🧠 4. 使用 shouldComponentUpdate(类组件)

对于类组件,可以手动控制组件是否需要更新。

shouldComponentUpdate(nextProps, nextState) {
  return nextProps.value !== this.props.value;
}

也可以继承 React.PureComponent,它会帮你做浅比较。

🧠 5. 避免匿名函数和内联对象作为 props

// ❌ 这种每次 render 都会生成新的函数和对象
<MyComponent onClick={() => {}} style={{ color: "red" }} />
​
// ✅ 用 useCallback 和 useMemo
const onClick = useCallback(() => {}, [])
const style = useMemo(() => ({ color: "red" }), [])
<MyComponent onClick={onClick} style={style} />

🧠 6. 拆分组件,让局部更新不影响整体

把组件拆小一点,避免父组件更新时,所有子组件都被迫重渲染。

🧠 7. 避免不必要的 setState

例如:

if (value !== newValue) {
  setValue(newValue);
}

✅ 总结:避免不必要 render 的常见方式

方法使用场景
React.memo缓存组件,props 没变就不渲染
useMemo缓存计算结果(如 filter/map/sort)
useCallback缓存函数引用,避免子组件误判
PureComponent / shouldComponentUpdate类组件控制更新
拆组件局部更新更高效
精准控制 setState减少无意义的状态更新

4、Webpack的打包过程

Webpack 的打包过程就像一个流水线,把你的项目代码“加工处理”成浏览器能直接运行的格式。我们可以从宏观上分 5 个阶段来理解整个打包流程:

1. 初始化阶段(Initialization)

  • 读取配置文件(webpack.config.js)
  • 合并默认配置和用户配置
  • 初始化编译器(Compiler)对象
  • 注册插件(调用 plugins.apply())
module.exports = {
  entry: './src/index.js',
  output: { filename: 'bundle.js' },
  plugins: [new MyPlugin()]
}

2. 构建依赖图(Build)

  • 从 entry 开始,调用对应的 Loader 处理模块
  • 解析依赖:比如 import, require() 等
  • 把依赖的模块也加入依赖图
  • 递归处理所有依赖模块

最终会生成一张完整的依赖关系图(Dependency Graph)


3. 模块转换(Transform)

  • 所有模块交给 Loader 处理(如 Babel 转换 ES6)
  • 每个模块变成 Webpack 能识别的格式(CommonJS)
module: {
  rules: [
    { test: /.js$/, use: 'babel-loader' },
    { test: /.css$/, use: ['style-loader', 'css-loader'] }
  ]
}

4. 生成 Chunk(代码块)

  • 按照依赖图,把模块组合成不同的 Chunk

    • 主 Chunk(入口)+ 异步 Chunk(懒加载)
  • 插件可以介入修改 Chunk 内容


5. 输出阶段(Emit)

  • Webpack 根据 output 设置生成最终文件

    • 比如 bundle.js、index.html、图片等
  • 插件如 HtmlWebpackPlugin 会生成 HTML 文件并注入打包好的 JS

  • 写入硬盘或内存中(如 webpack-dev-server)

output: {
  filename: '[name].[contenthash].js',
  path: path.resolve(__dirname, 'dist')
}

5、常见的状态码

🟢 1xx 信息响应类

状态码含义
100Continue,请继续请求
101Switching Protocols,协议切换

🟩 2xx 成功响应类

状态码含义
200OK,请求成功
201Created,资源创建成功(常见于 POST)
204No Content,请求成功但无返回内容(常见于 DELETE)

🟨 3xx 重定向响应类

状态码含义
301永久重定向(资源永久移动)
302临时重定向
304Not Modified,资源未修改(用于缓存)

🟥 4xx 客户端错误类

状态码含义
400Bad Request,请求语法错误
401Unauthorized,未认证(需要登录)
403Forbidden,已认证但没权限
404Not Found,资源不存在
405Method Not Allowed,不支持的请求方法

🟥 5xx 服务器错误类

状态码含义
500Internal Server Error,服务器内部出错
502Bad Gateway,网关错误
503Service Unavailable,服务器当前无法处理请求(比如宕机、限流)
504Gateway Timeout,网关超时

如果您觉得这篇文章有帮助,请点个赞吧~

分享文章

相关文章

更多文章 →
八股文2026-08-20
Embedding 向量模型:从语义表示到相似度计算
前言 大模型「读懂」文字靠的是 token,但 token 之间只有离散的编号关系,模型并不知道「苹果」和「李子」在语义上很近。要让程序能「理解」两段文字的相似程度,必须先把文本映射成一个 高维向量 ,再用几何方法比较。这一步就是 Embedding。 本篇基于我本地 今天的真实代码,从语义表示讲到余弦相似度,并复盘几个踩过的真实坑。 一、为什么需要 Embedding 传统关键词检索是「字面匹配」: Embedding 做的是「语义匹...
面试
八股文2026-08-19
前端面试100题
前端面试 100 题 适用方向:中高级前端 / React / Vue / Next.js / Nuxt / TypeScript / 工程化 / 实时通信 / Electron / Node.js / AI 应用前端 使用方式:优先掌握“标准回答”,再练“面试官追问”,最后把“结合你的简历怎么答”组织成自己的项目故事。 说明 “结合你的简历怎么答”只使用你简历中已经出现的项目与技术事实。 “标准回答 / 追问”属于通用前端知识总结,用...
面试
八股文2026-08-19
深入理解 JavaScript 原型与原型链:从关系图到核心逻辑 - heshanwan - 博客园
在 JavaScript 世界里, 原型(Prototype)和原型链(Prototype Chain) 是理解对象继承、属性查找机制的基石。很多开发者初学时对它们 “又爱又恨”,这篇文章将结合经典关系图,用通俗易懂的方式拆解原型与原型链的核心逻辑,帮你彻底掌握这套机制! &nbsp; 一、先搞懂几个核心概念 在分析关系图前,先明确 JavaScript 中与原型相关的关键概念,避免后续混淆: 1\. 函数对象与普通对象 函数对象 :由...
面试
八股文2026-08-17
JavaScript 闭包
摘要 :闭包(Closure)是 函数与其词法环境(Lexical Environment)的组合 ——一个函数可以"记住"并访问它被创建时所处的外部作用域中的变量,即使它在该作用域之外被执行。一句话记忆: 函数打包了出生地的变量,走到哪都能用 。核心要点:① 内部函数引用外部变量即形成闭包;② 闭包实现变量私有化/数据封装/柯里化;③ 潜在风险是内存泄漏和循环引用;④ 避免泄漏需手动解除引用和及时移除监听器。 一、什么是闭包 1.1...
面试
八股文2026-08-17
this 指向与 call / apply / bind
第 1 段(10min)回顾与预习:为什么必须搞懂 this 1.1 先接昨日(闭包)的线 昨天你学了 闭包 。闭包解决的是"函数记住它定义时所在作用域里的变量"。 闭包回答的问题是: “这个函数能访问到哪些变量?” → 看定义位置(词法作用域)。 而今天的主角 回答的是一个完全不同的问题: “这个函数执行时,站在谁的角度说话?” → 看 调用位置 (谁调用的、怎么调用的)。 一句话概括两者的分工: 变量看 定义处 , 看 调用处 。这...
面试
八股文2026-08-17
事件循环与宏任务 / 微任务
第 1 段(10min)回顾与预习:接住昨天的线 1.1 先接昨日(原型链)的线 昨天你学了 对象怎么找属性 (沿原型链上溯)。今天换一个维度: 代码怎么排队执行 。 把前三天的知识串成一句话,你已经有了 JS 的三张地图: 1.2 先凭直觉回答 4 个小问题(不用对,先写下答案) 学完第 2、3、4 段再回来对照。第 1 段只做一件事:记住画面—— JS 单线程,但靠"排队 + 轮转"假装多线程 。 第 2 段(15min)知识块①:...
面试

评论

请登录后发表评论

去登录
加载评论中...

目录