首页/文章/八股文

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

2025-09-15
290610 分钟
...

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 节点上都添加事件监听器,而是:

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

  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.jsindex.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-27
定时器按顺序播放多个音频,切后台音频会乱
本身不会补触发,但 它会被系统级冻结 ——iOS Safari 后台完全停止计时,切回前台后 只补执行一次 (不是不补,而是"缺失的中间状态补不上")。间隔短的不会出大问题, 间隔长的会出现"音频流被压缩" ——切回来后本该播 5 分钟的间隙,实际只过了 3 分钟,结果整段对不上。 核心原则 :定时器只能用来"提醒一次", 真正决定"该不该播"的是绝对时间戳 。 setTimeout 在后台会发生什么(按平台) | 平台 | 后台行为...
面试
八股文2026-08-27
线上项目白屏的原因
白屏的本质是 渲染管线某一环断了 ——可能是资源、JS、接口、路由、样式、兼容性任一环节出问题。排查按"控制台 → 网络 → DOM → 环境"四步定位。 本质(前端类比) 把网页想成一栋楼: 白屏 ≠ 一定是同一种原因——这是面试想听的层次。 六大类原因(按出现频率) | 类别 | 典型表现 | 真实案例 | | : | : | : | | ① 资源加载失败 | DOM 是空的 | 入口 JS 404、CDN 挂了、CSS 阻塞 |...
面试
八股文2026-08-27
背景图就是 1MB 大图,怎么优化
1MB 大背景图优化分 三步 :压缩体积(10 30x)、按需加载(按设备/视口/网速)、渲染期优化(GPU 合成)。背景图跟 不同——它是 CSS,不走浏览器的原生懒加载机制,得手动处理。 背景图 vs 的关键区别(先讲清楚这个) 这是面试官想听的"针对性认知"——背景图不是普通图片,不能套通用方案。 三步优化(按优先级) 第 1 步:压缩体积(最重要,立竿见影) 1MB 的来源一般是这几种 ,对应解决方案: | 原始问题 | 体积来...
面试
八股文2026-08-27
项目里很多图片和视频,怎么优化
图片视频优化分 四层 :网络层(CDN/格式)、加载层(懒加载/预加载)、渲染层(解码/缓存)、业务层(按需/降级)。面试要把这四层都讲到位才算有体系。 四层优化模型 第 1 层:网络层(省钱、省时间) 核心目标:让资源体积小、让用户拿到资源快 | 手段 | 作用 | 关键点 | | : | : | : | | 图片格式 | WebP/AVIF 比 JPEG 小 25 50% | 兼容 fallback | | 视频格式 | H.265...
面试
八股文2026-08-20
Embedding 向量模型:从语义表示到相似度计算
前言 大模型「读懂」文字靠的是 token,但 token 之间只有离散的编号关系,模型并不知道「苹果」和「李子」在语义上很近。要让程序能「理解」两段文字的相似程度,必须先把文本映射成一个 高维向量 ,再用几何方法比较。这一步就是 Embedding。 本篇基于我本地 今天的真实代码,从语义表示讲到余弦相似度,并复盘几个踩过的真实坑。 一、为什么需要 Embedding 传统关键词检索是「字面匹配」: Embedding 做的是「语义匹...
面试
八股文2026-08-19
前端面试100题
前端面试 100 题 适用方向:中高级前端 / React / Vue / Next.js / Nuxt / TypeScript / 工程化 / 实时通信 / Electron / Node.js / AI 应用前端 使用方式:优先掌握“标准回答”,再练“面试官追问”,最后把“结合你的简历怎么答”组织成自己的项目故事。 说明 “结合你的简历怎么答”只使用你简历中已经出现的项目与技术事实。 “标准回答 / 追问”属于通用前端知识总结,用...
面试

评论

请登录后发表评论

去登录
加载评论中...

目录