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

每次面对无穷无尽、不停迭代的前端知识,只想祈求秦始皇复活,来统一前端这些框架!学不完,根本学不完。但是为了找工作还是得老老实实背八股。背过的八股,不叫八股,是生产力!

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

✅ 1. 事件绑定方式不同

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

xml 复制代码
<button onClick={handleClick}>点我</button>

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

ini 复制代码
<button onclick="handleClick()">点我</button>

或者在 JS 中绑定:

arduino 复制代码
element.addEventListener('click', handleClick)

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

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

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

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

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

scss 复制代码
function handleClick(e) {
  e.preventDefault(); // 阻止默认行为
  e.stopPropagation(); // 阻止事件冒泡
}

✅ 4. 事件解绑方式不同

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

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

javascript 复制代码
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 中注册的事件,
    • 找到对应的组件方法,调用你写的事件处理函数。

🧪 举个例子
javascript 复制代码
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 是最常用的优化方式。

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

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

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

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

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

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

ini 复制代码
const handleClick = useCallback(() => {
  doSomething();
}, [dependency]);

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

🧠 4. 使用 shouldComponentUpdate(类组件)

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

javascript 复制代码
shouldComponentUpdate(nextProps, nextState) {
  return nextProps.value !== this.props.value;
}

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

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

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

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

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

🧠 7. 避免不必要的 setState

例如:

scss 复制代码
// ❌ 其实 value 没变,就没必要调用 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()
css 复制代码
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)
bash 复制代码
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

css 复制代码
output: {
  filename: '[name].[contenthash].js',
  path: path.resolve(__dirname, 'dist')
}

5、常见的状态码

🟢 1xx 信息响应类

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

🟩 2xx 成功响应类

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

🟨 3xx 重定向响应类

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

🟥 4xx 客户端错误类

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

🟥 5xx 服务器错误类

状态码 含义
500 Internal Server Error,服务器内部出错
502 Bad Gateway,网关错误
503 Service Unavailable,服务器当前无法处理请求(比如宕机、限流)
504 Gateway Timeout,网关超时
相关推荐
Senar4 分钟前
Web端选择本地文件的几种方式
前端·javascript·html
南客先生22 分钟前
互联网大厂Java面试:RocketMQ、RabbitMQ与Kafka的深度解析
java·面试·kafka·rabbitmq·rocketmq·消息中间件
烛阴22 分钟前
UV Coordinates & Uniforms -- OpenGL UV坐标和Uniform变量
前端·webgl
姑苏洛言26 分钟前
扫码小程序实现仓库进销存管理中遇到的问题 setStorageSync 存储大小限制错误解决方案
前端·后端
烛阴37 分钟前
JavaScript 的 8 大“阴间陷阱”,你绝对踩过!99% 程序员崩溃瞬间
前端·javascript·面试
lh_12541 小时前
ECharts 地图开发入门
前端·javascript·echarts
jjw_zyfx1 小时前
成熟的前端vue vite websocket,Django后端实现方案包含主动断开websocket连接的实现
前端·vue.js·websocket
Mikey_n2 小时前
前台调用接口的方式及速率对比
前端
周之鸥2 小时前
使用 Electron 打包可执行文件和资源:完整实战教程
前端·javascript·electron
我爱吃朱肉2 小时前
HTMLCSS模板实现水滴动画效果
前端·css·css3