React面试题

发布于:2025-07-29 ⋅ 阅读:(22) ⋅ 点赞:(0)

1.对React的理解、特性?

React 是靠数据驱动视图改变的一种框架,它把界面拆成可复用的 组件,通过 状态(state/props)=> 虚拟 DOM => Diff 算法 精准更新真实 DOM,让开发者只关心数据,不用手动操作页面元素

类别

特性

一句话解释

范式

声明式

描述“长什么样”,而非“怎么改”

组件化

Class / Function 组件

一切皆组件,粒度可大可小

渲染机制

Virtual DOM + Diff

用 JS 对象描述 UI,最小化真实 DOM 操作

数据流

单向数据流

数据从父 → 子 props,子 → 父 callback

状态管理

useState / useReducer / Context

组件内、跨组件共享状态

副作用

useEffect / useLayoutEffect

把“非纯”逻辑抽离出渲染阶段

性能优化

memo / useMemo / useCallback / lazy / Suspense

避免不必要的渲染或加载

并发特性

Concurrent Rendering

React 18 的时间切片让高优更新不阻塞低优更新

服务端渲染

SSR / Next.js / ReactDOMServer

首屏直出 + 同构

生态

Hooks、DevTools、Redux、React-Router、Testing Library

完整前端开发生态

2.React的生命周期?

3.对fiber的理解?

4.Diff算法?

5.hooks是什么?常用的hooks?

Hooks 是 React 提供的函数式 API,让函数组件拥有状态、生命周期等能力

使用规则:Hooks 只能在函数组件或自定义 Hook 顶层调用,不能放在循环、条件或普通函数里;依赖数组必须写全,自定义 Hook 需以 use 开头,否则 ESLint 会报错。

为什么:React 用链表顺序保存 Hook 状态;顺序一旦被打乱(条件、循环、嵌套),就会取错值或崩溃,所以必须放在顶层。

Hook

作用

典型代码

useState

保存局部状态

const [count,set]=useState(0)

useEffect

处理副作用(网络、订阅、操作 DOM)

useEffect(()=>{ fetch() },[])

useLayoutEffect

类似 useEffect,但同步执行(在浏览器绘制前)

计算布局后再渲染

useMemo

缓存计算结果,避免重复昂贵计算

const val=useMemo(()=>heavy(n),[n])

useCallback

缓存函数引用,减少子组件不必要渲染

const cb=useCallback(()=>do(),[dep])

useRef

保存可变值DOM 引用

const inputRef=useRef(null)

useContext

跨层级消费上下文

const theme=useContext(ThemeCtx)

useReducer

复杂 state 逻辑

const [state,dispatch]=useReducer(reducer,init)

useId

React 18 生成唯一 id(SSR 友好)

const id=useId()

useTransition

React 18 非紧急更新插队

const [isPending,start]=useTransition()

useRef 是 React 提供的一个 “可跨渲染周期保存可变值” 的 Hook,常用于两件事:

  1. 拿到 DOM 元素
  2. 存任意可变值(不触发重新渲染)
const ref = useRef<T>(initialValue);
ref.current 才是真实值
修改 ref.current 不会触发组件重新渲染

6.常用的api?

API

作用

示例

React.memo

函数组件浅比较缓存

export default memo(Button)

React.lazy + Suspense

代码分割 + 异步加载

const Page = lazy(() => import('./Page'))

React.createContext

跨层级共享数据

const ThemeCtx = createContext('light')

React.cloneElement

克隆并添加新 props

cloneElement(child, { className: 'active' })

React.Children.map/forEach

遍历 this.props.children

Children.map(children, c => cloneElement(c))

React.forwardRef

将 ref 转发到子组件

const MyInput = forwardRef((props, ref) => <input ref={ref} />)

React.isValidElement

判断是否为 React 元素

isValidElement(<div />) // true

前端开发中,“API”通常指的是浏览器提供的原生接口以及一些流行的第三方库或框架提供的接口,用于与Web平台进行交互。以下是一些常用的前端API分类及其简要说明:

浏览器提供的原生API

  1. DOM操作API
    • document.getElementById(), document.querySelector(): 选择页面元素。
    • element.innerHTML, element.textContent: 获取或设置元素的内容。
    • element.setAttribute(), element.getAttribute(): 设置或获取元素的属性。
  1. 事件处理API
    • addEventListener(): 添加事件监听器。
    • removeEventListener(): 移除事件监听器。
    • dispatchEvent(): 触发自定义事件。
  1. Fetch API
    • fetch(): 发起网络请求(GET, POST等),替代了传统的XMLHttpRequest
    • 示例:
fetch('https://api.example.com/data')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('Error fetching data:', error));
  1. History API
    • history.pushState(), history.replaceState(): 修改历史记录条目而不刷新页面。
    • window.onpopstate: 监听浏览器返回/前进按钮点击事件。
  1. Storage API
    • localStorage: 提供持久化的存储机制。
    • sessionStorage: 在会话期间保持数据。
    • 示例:
localStorage.setItem('key', 'value');
let value = localStorage.getItem('key');
  1. Geolocation API
    • navigator.geolocation.getCurrentPosition(): 获取用户的当前位置。
    • 示例:
navigator.geolocation.getCurrentPosition(position => {
  console.log(`Latitude is ${position.coords.latitude}`);
});
  1. Canvas API
    • <canvas> 标签配合 JavaScript 绘制图形、动画等。
    • 示例:
const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');
ctx.fillStyle = 'green';
ctx.fillRect(10, 10, 150, 100);
  1. File API
    • 处理文件输入和读取文件内容。
    • 示例:
const fileInput = document.querySelector('input[type="file"]');
fileInput.addEventListener('change', () => {
  const file = fileInput.files[0];
  const reader = new FileReader();
  reader.onload = function(e) {
    console.log(e.target.result); // 文件内容
  };
  reader.readAsText(file);
});
  1. Notification API
    • 显示桌面通知。
    • 示例:
if (Notification.permission === 'granted') {
  new Notification('Hello!');
} else if (Notification.permission !== 'denied') {
  Notification.requestPermission().then(permission => {
    if (permission === 'granted') {
      new Notification('Hello!');
    }
  });
}

第三方库或框架提供的API

  • Axios: 一个基于Promise的HTTP客户端,用于浏览器和Node.js发起异步请求。
  • Lodash/Underscore: 提供实用工具函数,如数组和对象的操作。
  • Moment.js(注意:此库已进入维护模式,推荐使用更轻量级的选择如date-fns): 用于解析、验证、操作及格式化日期。
  • React/Vue/Angular等框架提供的API: 这些框架提供了各自的状态管理、组件生命周期钩子等API。

7.常用的库?

1. 基础三件套(99 % 项目必用)

作用

一句话代码

React

视图层

import { useState } from 'react'

Vue 3

视图层

import { ref } from 'vue'

TypeScript

类型系统

npm i -D typescript

2. 状态管理 & 路由

场景

典型用法

Redux / Toolkit

全局复杂状态

configureStore({ reducer })

Zustand

轻量全局状态

const useStore = create(set => ({ count: 0 }))

Pinia

Vue 官方状态库

defineStore('main', { state: () => ({}) })

React-Router v6

React SPA 路由

<Route path="/" element={<Home />} />

Vue-Router 4

Vue SPA 路由

{ path: '/', component: Home }

3. 网络与请求

场景

示例

axios

HTTP 请求

axios.get('/api/user')

SWR / React-Query

数据获取 + 缓存

const { data } = useSWR('/api', fetcher)

GraphQL

查询语言

import { gql } from 'graphql-tag'

4. UI 组件库

技术栈

一行引入

Ant Design

React

import { Button } from 'antd'

Element Plus

Vue 3

import { ElButton } from 'element-plus'

Mantine / Chakra UI

React

import { Button } from '@mantine/core'

Tailwind CSS

原子化样式

className="bg-blue-500"

5. 构建与工具链

库 / 工具

作用

命令

Vite

极速构建

npm create vite@latest

Webpack

老牌打包

npm i -D webpack

ESLint + Prettier

代码规范

npm i -D eslint prettier

PostCSS

CSS 后处理

npm i -D postcss autoprefixer

6. 测试 & 质量

场景

示例

Jest

单元测试

test('add', () => expect(1+1).toBe(2))

Vitest

Vite 原生测试

npm i -D vitest

Cypress / Playwright

端到端

cy.visit('/login')

7. 数据可视化 & 动画

场景

示例

ECharts

图表

echarts.init(dom).setOption(option)

D3.js

高级可视化

import * as d3 from 'd3'

Framer-Motion / GSAP

动画

motion.div animate={{ x: 100 }}

8. 实用小工具

作用

示例

Lodash / Radash

工具函数

_.debounce(fn, 300)

Day.js / date-fns

日期处理

dayjs().format('YYYY-MM-DD')

Zod / Yup

运行时校验

const schema = z.string()

8.react组件通信方式?React 组件通信 = “单向数据流 + 跨层级/兄弟需求” 的 6 大方案,面试按场景说即可。

✅ 1. 父 → 子(最常见)

方式

代码片段

描述

props

<Child msg={msg} />

父组件把数据通过属性下发

✅ 2. 子 → 父

方式

代码片段

描述

回调函数

<Child onChange={setVal} />

子组件调用父组件传的回调,把值带回去

✅ 3. 跨多级/任意层级(避免逐层传递)

方式

代码片段

场景

Context

const Ctx = createContext()

主题、语言、登录态等全局数据

Redux / Zustand / Pinia

const val = useSelector(state => state.x)

复杂全局状态

EventBus / PubSub

eventBus.emit('login')

兄弟组件通信(简单项目)

✅ 4. 兄弟组件

方式

思路

代码片段

公共父组件

把状态提升到最近父级

const [val, setVal] = useState()

全局状态库

直接读/写全局 store

dispatch({ type: 'update' })

✅ 5. ref / forwardRef(父拿子实例)

方式

代码片段

场景

forwardRef + useImperativeHandle

const childRef = useRef()

父组件需要直接调用子组件方法(如聚焦输入框)

✅ 6. URL / 路由传参

方式

代码片段

场景

React-Router

<Link to="/user/123">

页面间切页通信

🎯 面试 15 秒回答

“React 通信遵循单向数据流;父子用 props + 回调,跨层级用 Context,复杂全局用 Redux/Zustand,兄弟可提升状态或用 EventBus,父调子用 forwardRef + ref。”

9.虚拟dom和普通dom的区别?

特性

真实DOM

虚拟DOM

定义

浏览器中用于表示网页文档的对象模型,直接反映页面结构。

内存中的轻量级JavaScript对象,是真实DOM的抽象表示。

更新效率

更新成本高,每次DOM变更都会引起浏览器重新计算布局、样式及绘制。

通过Diffing算法比较新旧虚拟DOM树的不同,只更新实际变化的部分到真实DOM。

编程模型

命令式编程,开发者需明确指定如何修改UI。

声明式编程,描述期望的状态,框架决定如何最有效地实现这种状态转换。

性能影响

频繁的DOM操作可能导致性能瓶颈,特别是在处理大量元素时。

批量更新并在内存中完成大部分工作,减少了不必要的重绘和回流。

复杂度与维护性

直接操作DOM可能导致代码难以维护,尤其是在UI复杂的情况下。

提供更好的抽象层,使得代码更加模块化、易于理解和维护。

应用场景

适用于简单的网页或不需要频繁更新的应用。

在构建交互性强、数据驱动的单页应用(SPA)时特别有用。

真实DOM是浏览器用来展示网页内容的对象模型,它是一个树形结构,每个节点代表页面上的一个HTML元素。当我们需要动态更新页面内容时,直接操作真实DOM会导致较高的性能开销,因为每一次DOM变更都会触发浏览器重新计算布局、样式以及绘制页面。

相比之下,虚拟DOM是一种轻量级的JavaScript对象,它是真实DOM的一个抽象表示。现代前端框架如React使用虚拟DOM来优化用户界面的更新过程。当应用程序状态发生变化时,框架首先在虚拟DOM上进行相应的更改,然后通过Diffing算法找出新旧虚拟DOM树之间的差异,最后仅将这些差异应用到真实DOM上,从而大大减少了不必要的重绘和回流,提高了性能。

10.从setState到页面渲染经历了什么?


setState → 调度 → Diff → Commit → 绘制,React 18 再加并发调度

1. 触发阶段

  • 执行 this.setState / useState setter
    → 生成新的 state 快照
    → React 把这次更新包装成一个 update 对象 放进当前 Fiber 的 更新队列

2. 调度阶段(Scheduler)

  • React 18 的 Concurrent Scheduler 根据优先级(同步/用户阻塞/普通/低优)
    → 将更新任务切片(时间切片),
    → 使用 MessageChannel / setTimeout 让出主线程,防止长任务阻塞。

3. 渲染阶段(Render Phase,可中断)

  • 从根节点开始 深度遍历 Fiber 树
  • 调用函数组件 → 得到新的 虚拟 DOM(JSX → VNode)
  • Diff 算法(双指针 + key):
    1. 比较相同层级节点
    2. 标记副作用(新增/删除/移动/更新)
    3. 生成 Effect List 链表。
  • 整个过程 可中断、可复用(React 18 并发特性)。

4. Commit 阶段(不可中断)

  • 同步执行副作用:
    DOM 插入/更新/删除(对应 Effect List)
    调用生命周期 / Hook 副作用useLayoutEffectuseEffect
    更新 Ref
    触发合成事件更新浏览器 DOM

5. 浏览器绘制

  • React commit 结束后 把变更同步到真实 DOM
  • 浏览器执行 重排、重绘、合成 → 像素上屏。

6. React 18 额外流程

新特性

插入位置

startTransition

调度阶段把更新标为低优

Suspense SSR

流式 HTML + Selective Hydration

Automatic Batching

调度阶段合并多次 setState

面试 15 秒速答

setState 把更新放进 Fiber 队列 → Scheduler 按优先级切片 → Render 阶段可中断地 Diff → Commit 阶段同步更新真实 DOM → 浏览器重排重绘完成页面刷新。

切片:


React 把一次大任务切成很多小片,每片默认 5 ms,让浏览器在两次切片之间有空隙去做别的(响应用户输入、动画等)。
怎么做:

  • 底层用 MessageChannelsetTimeout 把任务挂起 → 让出主线程 → 下帧再继续。
    效果:
  • 长任务不再卡死页面(避免掉帧)。
  • 高优先级更新(点击、输入)可以插队低优先级更新(列表加载)。
// 伪代码:React 内部
while (任务未完成 && 时间 < 5ms) {
  执行一个切片
}
if 未完成 → 让出主线程 → 下帧继续

2. Commit(提交阶段)


已算好的 DOM 变更一次性同步写入浏览器,不可中断。
包含三件事:

  1. DOM 插入 / 更新 / 删除
  2. 调用生命周期 / 副作用useLayoutEffectuseEffect
  3. 更新 Ref、同步浏览器 DOM

为什么不可中断:
真实 DOM 操作、生命周期副作用必须原子执行,否则会出现半更新、闪烁或事件错乱。

11.React数据流?redux原理是什么?

在React中,数据流通常是指组件之间的数据传递方式。React采用单向数据流(unidirectional data flow),这意味着数据只能从父组件流向子组件,通常通过props实现。这种设计使得应用的状态管理更加直观和可预测。

React中的数据流

  1. 父组件到子组件:通过props将数据从父组件传递给子组件。
  2. 状态提升:当多个组件需要共享相同的数据时,可以将这些组件的状态提升到它们最近的共同祖先组件中管理。
  3. 回调函数:为了实现父组件与子组件之间的通信,父组件可以通过props传递一个回调函数给子组件,子组件调用这个函数来更新父组件的状态。

然而,随着应用规模的增长,管理全局状态变得越来越复杂。这时就需要使用专门的状态管理库,如Redux。

Redux原理

Redux是一个用于JavaScript应用程序的状态管理库,通常用于管理React应用中的全局状态。它的设计理念来源于Flux架构,但比Flux更简单且更容易理解。以下是Redux的核心概念:

  1. 单一数据源:整个应用的状态存储在一个称为store的对象中。这确保了应用的状态是集中管理的,并且是唯一的事实来源。
  2. State是只读的:唯一改变state的方法是触发action。Action是对已发生事情的普通对象描述,它必须包含一个type字段来描述发生了什么。
  3. 使用纯函数Reducer来修改状态:Reducer是一个纯函数,它接收当前的state和一个action作为参数,返回一个新的state结果。Reducer根据action的类型来决定如何更新state。
  4. Store:Store是保存数据的地方,负责提供getState()方法获取state、dispatch(action)方法更新state以及subscribe(listener)方法添加监听器。
  5. 中间件支持异步逻辑:虽然Redux的核心机制是同步的,但是通过中间件如redux-thunk或redux-saga,可以处理异步操作。

中间件:在Redux中,中间件主要用于处理异步逻辑、日志记录、异常捕获等任务。Redux中间件提供了一种机制来拦截并处理action,在action被reducer处理之前对其进行修改、延迟、替换或者执行副作用(如API调用)等操作。

  • 发起异步请求(AJAX、GraphQL、WebSocket)
  • 做权限校验、埋点、异常上报
  • 串联多个 action(thunk、saga、observable)

这些副作用和横切关注点如果都写进组件或 reducer,会破坏 Redux “纯函数 + 单向数据流” 的设计。
于是 Redux 提供了一个扩展点:在 action 发出之后、到达 reducer 之前,插入可组合的中间件链

Redux 中间件的定位:

  1. 拦截的是 action 对象,而不是 HTTP 请求。
  2. 运行在 Store 的 dispatch 阶段,每一个中间件都能:
    • 拿到当前的 getState
    • 拿到当前被 dispatch 的 action
    • 可以调用 next(action) 把 action 继续往下传
    • 可以调用 dispatch(anotherAction) 触发新的 action
    • 甚至可以根本不调用 next,从而“吞掉”某个 action

名称

解决什么问题

一句话描述

redux-thunk

支持 action 为函数,方便做异步流程

“dispatch 一个函数而不是对象”

redux-saga

用 ES6 Generator 描述复杂副作用

“把副作用写成可测试的 saga”

redux-observable

用 RxJS 把 action 当流处理

“action

redux-promise

支持 dispatch Promise,自动 resolve/reject

“dispatch(promise)”

redux-logger

开发阶段打印 action 和 state 变化

见上文

Redux 异步操作指南:使用 redux-thunk 和 redux-saga-CSDN博客

12.tsx转化为真实dom的过程?

TSX 只是写 UI 的语法糖 → 编译成 JS 对象 → React 的 Fiber 架构在时间切片里生成/更新虚拟树 → 同步 Commit 阶段一次性把最小 DOM 操作刷到浏览器 → 浏览器再把它画成像素。

TSX 源码

↓ [TypeScript + Babel]

JSX 对象 (ReactElement)

↓ [React 18 Fiber Reconciler - beginWork]

Fiber 树 (虚拟节点,可中断)

↓ [commitMutationEffects]

真实 DOM 树插入文档

↓ [浏览器]

重排 → 重绘 → 合成 → 像素

13.受控组件和非受控组件?

受控组件(controlled)是React 数据绑定,非受控组件(uncontrolled)是DOM 数据自管

一般都用受控组件,性能问题或者需要一次性取值的时候用非受控

const [name, setName] = useState('');
return (
  <input
    value={name}
    onChange={e => setName(e.target.value)}
  />
);
const inputRef = useRef<HTMLInputElement>(null);
const handleSubmit = () => {
  console.log(inputRef.current?.value); // 一次性取值
};
return (
  <>
    <input ref={inputRef} defaultValue="Tom" />
    <button onClick={handleSubmit}>提交</button>
  </>
);

14.React高阶组件?

React 高阶组件(HOC, Higher-Order Component)就是一个纯函数,它接收一个组件、返回一个新的组件,用来横向抽离公共逻辑(权限、埋点、数据获取、样式注入等),本质属于装饰器模式

// withLoading.jsx
import React from 'react';

// HOC:函数接收一个组件,返回一个新组件
function withLoading(WrappedComponent) {
  // 新组件
  return function WithLoading({ isLoading, ...rest }) {
    if (isLoading) {
      return <div style={{ padding: 20 }}>🌀 加载中...</div>;
    }
    // 透传剩余 props
    return <WrappedComponent {...rest} />;
  };
}

export default withLoading;

15.React的批处理?

React 批处理(batching)就是把多次 setState 合并成一次重渲染,避免 N 次 DOM 更新;React 18 之前只在事件回调里自动批,18 之后默认全部批(包括 Promise、setTimeout、原生事件等)

React 维护一个更新队列(update queue)

  1. 每次 setState 只是把更新对象推进队列,不立即 render。
  2. 当前调用栈清空(事件结束 / micro-task 结束)时,scheduler 把队列合并后一次性 render。
  3. 18 之后通过 自动批处理机制 (Automatic Batching) 把“所有来源的更新”统一放到下一帧前批量执行。

16.React-Router的实现原理?

17.useState的实现原理?

注册状态:

当React在渲染函数组件的过程中遇到useState(initialState)时,它会在组件实例的内部创建一个新的状态槽(slot),并将initialState作为初始值存入该槽位。由于函数组件没有实例的概念,React通过Fiber节点来模拟实例行为,所以实际上是给对应的Fiber节点添加了状态。

状态钩子的创建:

React会返回一个状态对——当前状态值和更新函数。状态值就是从状态槽中读取的初始值;更新函数则是由React自动生成的一个闭包函数,它能够找到正确的状态槽并更新其值。

状态更新:

当调用setState(newState)时,React不会立即修改状态,而是创建一个更新任务,将其加入到一个优先级队列中。这是因为React采用了异步更新策略,以批量处理多个状态更新,提高性能。

调度过程:

调度器(Reconciler/Renderer)会按照一定的优先级顺序处理队列中的更新任务。当轮到处理某个组件的状态更新时,调度器会查看新旧状态是否不同,如果不同,则标记该组件及其子孙组件为需要重新渲染。

渲染阶段:

在渲染阶段,React会重新执行函数组件体以获取最新的UI描述(虚拟DOM)。此时,useState会返回上一次渲染时的状态值,但如果在本次渲染周期中有待处理的更新,则会返回已更新的状态值。

差异比较与DOM更新:

React使用虚拟DOM diff算法找出前后两次渲染之间的差异,然后只对实际DOM进行必要的更新操作。

闭包的作用:

setState函数通过闭包绑定到了正确的组件实例(即Fiber节点),这样在任何地方调用这个函数都能够准确地修改相应组件的状态,而不影响其他组件。

18.useMemo和react.memo的区别?

React.memo 包组件,useMemo 包值;一个防止组件重渲染,一个防止计算重执行

维度

React.memo

useMemo

应用位置

组件定义外层 const Foo = React.memo(...)

函数体内 const value = useMemo(..., [...])

缓存粒度

整个组件渲染结果

单个值 / 计算结果

是否缓存 DOM

否,仅跳过 VDOM diff

否,仅缓存值

19.说一下react事件合成?

一句话先答
React 事件合成(Synthetic Event)就是用一套跨浏览器的“假事件对象”把原生事件包起来统一 API、统一行为、统一性能优化,所有你写的 onClickonChange 最终都挂到根节点(root)上,通过事件委托批量处理。


一、为什么需要合成

  1. 浏览器差异:IE 没有 event.targetstopPropagation 表现不一致等。
  2. 内存/性能:每个节点都绑监听会爆炸;React 只在顶层绑一个。
  3. 特性增强:onMouseEnter 在原生里根本不存在,React 通过合成模拟。
  4. 并发特性:React 18 的时间切片下,原生事件可能被中断;合成事件可以池化复用延迟处理

二、生命周期(面试讲 4 步即可)

  1. 挂载阶段
    • ReactDOM 在 document(或 createRoot 的 root)上注册一个 dispatchEvent 监听器,覆盖所有事件类型(click、input、keydown …)。
  2. 用户触发
    • 点击按钮 → 原生事件冒泡到 root。
    dispatchEvent 收到原生 MouseEvent
  3. 合成阶段
    • 生成 SyntheticEvent 实例:抹平浏览器差异,字段统一(event.targetevent.currentTargetevent.preventDefault()…)。
    • 放入事件池(React 17 已废弃池化,但概念仍在)。
  4. 回调执行
    • 根据事件路径(Fiber 树)收集所有对应组件上写的 onClick 回调。
    • 依次执行 → 触发 setState → 进入 React 调度。

三、代码级微观示意

// 伪代码
function dispatchEvent(nativeEvent) {
  const syntheticEvent = new SyntheticEvent(nativeEvent);
  const listeners = collectListeners(nativeEvent); // 根据 Fiber 树收集
  for (const listener of listeners) {
    listener(syntheticEvent); // 你写的 onClick 在这儿被执行
  }
}

四、和原生事件的差异(面试高频追问)

场景

React 合成事件

原生事件

绑定位置

root 节点(事件委托)

具体 DOM 节点

默认冒泡

与原生一致,可用 e.stopPropagation()

同上

阻止默认行为

e.preventDefault()

同上

异步访问 event

React 17+ 已不池化,可放心保留

原生 event 一直可用

事件名写法

小驼峰 onClick

全小写 onclick

事件对象类型

SyntheticEvent(跨浏览器统一)

浏览器原生 Event


五、面试 30 秒总结
“React 事件合成 = 顶层统一监听 + 跨浏览器包装 + 事件池/委托
我们写的 onClick 并不是直接绑在按钮上,而是挂在 root;当点击冒泡进来后,React 生成合成事件对象,抹平差异,再按 Fiber 树收集回调执行,既省内存又避免浏览器兼容坑。”

20.说一下react状态提升?

在React中,状态提升(State Lifting)是一种管理状态(state)的设计模式,它是指当多个组件需要共享相同状态时,而不是各自在各自的组件内维护独立的状态副本,应该将该状态集中管理,将其移到这些组件的最近共同父组件(或创建一个新的容器组件专门用来托管此状态)中去维护。这样,父组件可以通过props向下传递状态以及更新状态的方法给子组件,子组件通过调用这些方法通知父组件来间接更改和同步状态,从而保证所有依赖此状态的子组件能够得到及时和一致的更新。

使用场景举例:

表单联动:例如有两个相关的输入框,当在一个输入框中输入内容时,另一个输入框的内容需要同步更新,这时就可以将这两个输入框的状态提升到它们的父组件中统一管理。

全局应用状态:比如导航栏的显示/隐藏状态、主题颜色、登录状态等需要在整个应用范围内共享的状态,这些状态通常会被提升到顶层组件(如App组件)中统一管理。

列表项的选择状态:在一个列表组件中有多个可勾选的子项,这些子项的选择状态需要被集中管理以便进行全选、反选等操作,这时可以把选择状态提升到列表组件中。

跨组件通信:当两个没有直接关联的子组件需要基于同一份数据进行操作时,可以通过将这份数据放入它们的公共父组件中来实现在更高层级上的状态控制。

通过状态提升,可以确保状态的一致性和避免组件之间的不必要的直接耦合,符合React中单向数据流的原则,使应用程序的数据流更易于理解和调试。


网站公告

今日签到

点亮在社区的每一天
去签到