首页
题库
公司真题
专项练习
面试题库
在线编程
面试
面试经验
AI 模拟面试
简历
求职
学习
基础学习课
实战项目课
求职辅导课
专栏&文章
竞赛
搜索
我要招人
发布职位
发布职位、邀约牛人
更多企业解决方案
AI面试、笔试、校招、雇品
HR免费试用AI面试
最新面试提效必备
登录
/
注册
牛客477642835号
门头沟学院 Web前端
发布于浙江
关注
已关注
取消关注
@来个offer吧!!!!:
React常见面试题汇总
众所周知,React是目前互联网大厂使用最多的前端框架,在面试过程中,面试官也更喜欢有React开发经验的同学。本文总结了React中一些重要的特性,同时也是在平时的面试中经常被问到的一些问题。希望对各位前端开发同学有所帮助,如果觉得还不错请多多点赞收藏。 目录 如何理解 Rect React与Vue的异同 如何理解虚拟 DOM React 生命周期 React 状态复用的方式 如何理解 React Hooks React Diff 算法 React fiber 架构的理解 react 性能优化的手段 setState 是同步的还是异步的 React 合成事件 如何理解 Rect react 是什么? react 是一个用于构建用户界面的 javascript 库。 为什么会出现 react 这样的框架 在我们早期的前端开发中,我们通过 JavaScript 来手动操作 DOM 以达到和用户交互的目的,但是原生的 DOM 操作繁琐并且还会存在兼容性问题。因此在我们前端开发中出现 jquery 这样的框架,来帮助我们简化 DOM 操作,同时解决了各种兼容性问题。但是随着我们的项目变得越来越复杂,我们发现,光有 DOM 操作远远不够,我们还要有应用结构,因此我们开始探讨前端组件化的问题,通过组件化的方式来让我们的项目结构变得更加清晰,同时也引入了 MVX 的概念。接着大家发现 MVX 的数据绑定依然需要我们手动监听 model 的变化,然后去做各种 DOM 操作,数据到视图层的映射依然繁琐。于是大家就开始推崇 MVVM 的数据绑定,于是就出现了例如 Angular,React 和 Vue 这样的前端框架。 react 的特点 react具有声明式,组件化以及一次学习,随处编写的特点,因此 react 特别适合用于构建快速响应的大型 web 应用 React与Vue的异同 相同点: 使用虚拟 DOM 提供响应式和组件化的视图组件 将注意力集中保持在核心库,而将其他功能如路由、全局状态管理交给相关的库 不同点: 视图更新 在react中,当某个组件的状态发生变化的时候,会以该组件为根,重新渲染整个组件树,如果要避免不必要的子组件重渲染,我们需要在所有可能的地方使用 PureComponent 或者是 shouldComponentUpdate 方法。 在 Vue 应用中,组件的依赖是在渲染过程中自动追踪的,所以vue能精确知晓哪个组件确实需要被重渲染。Vue的特点可以让开发者不再考虑此类优化,从而更好地专注于应用本身 HTML 和 CSS 在 react 中,everything is JavaScript,不仅是 html 可以用 jsx 表示,连 css 也有 js 的解决方案。 Vue 的整体思想是拥抱经典的 Web 技术,并在其上进行扩展。使用 html,css 和 js 来构建模板。 在这个方面我觉得 vue 是在适应开发者,而 react 想要改变开发者。 同时,在组件作用域内的 css 上,react 是使用的 css-in-js 的解决方案或者是 css modules。而 vue 使用 scoped 来控制 css 规模 vue 的路由管理和状态管理由官方维护,而 react 则是把这些问题交给社区,创建了一个更为分散的生态系统,因此react的生态系统相对于 vue 更加繁荣。 原生渲染 由于两者都是使用的虚拟 dom 技术,因此都可以构建原生的安卓或者 ios 应用,例如 react-native 和 weex 如何理解虚拟 DOM 虚拟 dom 是什么 虚拟 DOM 是对 DOM 的抽象,本质上是 JavaScript 对象 为什么需要虚拟dom 首先,我们知道在前端性能优化中,有一个很重要的一项就是尽可能少的操作 DOM,不仅仅是 DOM 操作相对较慢,更是因为频繁的 DOM 操作会造成浏览器的回流或者重绘,这些都会对我们的性能造成影响。因此我们需要一个抽象,在patch过程中,尽量一次将差异更新到 DOM 中。 其次,现代前端框架的一个基本要求就是无须手动操作DOM,这样可以大大提高开发效率。 最后,虚拟DOM可以更好地实现跨平台,用于编写原生应用。 React 生命周期 React < 16.3 组件的生命周期包含挂载或者更新组件过程中被触发的一系列方法。这些方法可以在组件渲染UI之前或者之后触发。以下是 React 16.3 之前的生命周期 挂载生命周期 constructor(props):初始化 props 和 State。 componentWillMount(): 一旦属性被获取并且也初始化了 State,该方法将会被触发。该方法是在 DOM 被渲染之前触发的,并且可以用来初始化第三方脚本库、启动动画、请求数据,以及其他可能需要在组件被渲染之前执行的额外步骤。还可以在该方法中触发 setState 方法,在组件被初次渲染之前修改组件的 State。 在组件被渲染完毕之前调用 setState 方法将不会启动更新生命周期。在组件渲染完毕之后调用 setState 方法就会启动更新生命周期。 如果用户在 componentWillMount 方法中定义的回调函数内调用了 setState 方法,那么它将会在组件被渲染完成之后被触发,并且会启动更新生命周期。 render():只要父组件 render 被调用,其所有子组件的 render 都会被调用 componentDidMount():组件被渲染完毕之后触发,可以获取到 DOM。 componentWillUnmount():组件被卸载之前触发,在这里可以清除一些后台进程。 更新生命周期 更新生命周期是当组件 State 发生变化或者从父组件接收到新的属性时触发的一系列方法。 更新生命周期会在每次调用 setState 方法后启动。调用 setState 方法过程内部的更新生命周期将会引发无限递归循环。因此只能在 componentWillReceiveProps 方法内部调用 setState 方法,它允许组件属性被更新后更新 State。 更新生命周期包括以下几个方法: componentWillReceiveProps(nextProps): 仅当新的属性被传递给了组件后才会调用。这也是唯一可以调用 setState 方法的地方。 shouldComponentUpdate(nextProps, nextState): 更新生命周期的门卫,一个可以取消更新操作的谓词。该方法可以通过只允许执行必须的更新操作来改进性能。 componentWillUpdate(nextProps, nextState): 只在组件更新之前触发。和 componentWillMount 类似,只是它会在每次更新操作之前被触发。 componentDidUpdate(prevProps, prevState): 只在更新操作发生后,调用 render 方法之后触发。类似 componentDidMount 方法,不过它会在每次更新之后触发。 React >= 16.4 React 大于等于 16.4 的生命周期图谱如下: 还有一些不常用的生命周期钩子: 这里我们着重介绍 getDerivedStateFromProps生命周期。需要注意的是,React 16.3 的版本中 getDerivedStateFromProps 的触发范围和 16.4^ 是不同的,主要区别在 setState 和 forceUpdate 时会不会触发。具体可以看生命周期图。 该生命周期的使用场景主要有两个: 无条件的根据 prop 来更新内部 state,也就是只要有传入 prop 值,就更新 state。 只有 props 值和 state 不同时才更新 state。 具体示例可以参考官方文档。 React 状态复用的方式 mixin mixin之间的相互依赖会形成依赖链,当我们改动其中一个mixin的状态的时候,很可能会直接影响其他的mixin,不利于代码维护 不同mixin的之间可能会产生命名冲突 增加组件的复杂性,当我们引入的mixin较多时,会让代码的逻辑变得十分复杂,难以维护。 高阶组件 HOC需要在原组件上进行包裹或者嵌套,如果大量使用HOC,将会产生非常多的嵌套,这让调试变得非常困难。 render props render props是一项通过props来告知组件需要渲染什么内容的技术,它的使用场景是什么呢? 很多时候我们渲染一个组件,但是它的逻辑和数据却依赖于父组件,这种情况下我们可以把那部分可以复用的逻辑抽取在父组件中,并且在父组件暴露一个参数 render 来接收渲染子组件的方法,并且通过这个方法把子组件所依赖的数据传给它,这种方式就是render props。 render props的使用思路是控制反转,子组件通过render props的方式定义好渲染自身的函数,父组件再把子组件需要的数据传给这个函数,子组件拿到数据后可以随意的渲染自身。 hooks 减少状态逻辑复用的风险,mixin引入的状态逻辑会相互覆盖,HOC也可能会出现 props 覆盖 避免层级嵌套,让组件更易于理解 使用函数代替 class ,更符合 react 函数式理念 问题 只能在顶层使用 hooks 只在 React 函数中使用 hooks 如何理解 React Hooks 是什么 hook 是 react16.8 的新增特性,它可以让你在不编写 class 的情况下使用 state 以及其他的 react 特性 解决了什么问题 react解决了我们在使用和维护 react 中经常遇到的问题 组件之间的状态和逻辑复用 以前使用 render props 和 高阶组件。但是这类方案需要重新组织你的组件结构,这可能会很麻烦,使你的代码难以理解。 复杂组件变得难以理解 我们经常维护一些组件,组件起初很简单,但是逐渐会被状态逻辑和副作用充斥。每个生命周期常常包含一些不相关的逻辑。例如,组件常常在 componentDidMount 和 componentDidUpdate 中获取数据。但是,同一个 componentDidMount 中可能也包含很多其它的逻辑,如设置事件监听,而之后需在 componentWillUnmount 中清除。相互关联且需要对照修改的代码被进行了拆分,而完全不相关的代码却在同一个方法中组合在一起。如此很容易产生 bug,并且导致逻辑不一致。 难以理解的 class class 组件中经常需要手动去绑定 this,因此需要我们去理解 js 中 this 的工作原理,增加了学习成本 有哪些 useState useEffect useRef useContext useMemo useCallback React Diff 算法 React的 diff 策略: 在 Web UI 中,跨层级移动节点的操作很少,可以忽略不计 拥有相同类的两个组件会生成相似的树形结构,拥有不同类的两个组件会生成不同的树形结构 对于同一层级的一组子节点,可以通过唯一 id 进行区分 基于以上策略,React 分别对 Tree diff、Component diff 以及 Element diff 进行了优化。 tree dif 在 tree diff 的时候,react 只会对两颗树的同一层级的节点进行比较,即同一个父节点下的所有子节点。当发现节点已经不存在,则该节点及其子节点会被完全删除掉,不会用于进一步的比较。这样只需要对树进行一次遍历,便能完成整个 DOM 树的比较。 如果出现了跨层级的移动,React会删掉该节点以及其所有的子节点。然后在新的节点下创建节点。 如下图,A 节点(包括其子节点)整个被移动到 D 节点下,由于 React 只会简单的考虑同层级节点的位置变换,而对于不同层级的节点,只有创建和删除操作。当根节点发现子节点中 A 消失了,就会直接销毁 A;当 D 发现多了一个子节点 A,则会创建新的 A(包括子节点)作为其子节点。此时,React diff 的执行情况:create A -> create B -> create C -> delete A。 只有删除、创建操作,没有移动操作 component diff React 是基于组件构建的应用,因此在进行 diff 时,react 遵循以下策略: 如果是同一类型的组件,则按照原策略继续比较 DOM tree 如果是不同类型的组件,react 会替换整个组件下的所有子节点 对于同一类型的组件,其虚拟dom可能并没有发生变化,因此react提供shouldComponentUpdate来让我们判断该组件是否需要diff。 如下图,当 component D 改变为 component G 时,即使这两个 component 结构相似,一旦 React 判断 D 和 G 是不同类型的组件,就不会比较二者的结构,而是直接删除 component D,重新创建 component G 以及其子节点。 element diff 当节点处于同一层级的时候,react diff 提供了三种节点操作,移动、删除、插入。 当节点没有 key 的时候,在对比新老虚拟DOM的时候,会出现频繁的创建,删除节点的操作,不能很好的做到节点复用,影响 diff 的效率。 因此我们在对同一层级的子元素进行比较的时候,需要添加唯一的key进行区分。 有了 key 之后,react diff 在同层级比较的时候, 首先会初始化 lastIndex 和 nextIndex,两者都为 0. 遍历新的虚拟dom集合,找到新的集合中的每个节点在老集合中的位置 oldIndex, 如果 oldIndex >= lastIndex,则该节点保持不动,并更新 lastIndex = Math.max(lastIndex, oldIndex),然后更新该节点的位置为 nextIndex,nextIndex ++ 如果 oldIndex < lastIndex,则移动该节点至 nextIndex 的位置,同时更新 lastIndex = Math.max(lastIndex, oldIndex),nextIndex ++ 如果老集合中没有找到节点,说明节点是新增的,则会创建新节点插入到 nextIndex 的位置 当完成新集合中所有节点 diff 时,最后还需要对老集合进行遍历,判断是否存在新集合中没有但老集合中仍存在的节点,发现存在这样的节点 D,因此删除节点 D,到此 diff 全部完成。 当然,React diff 还是存在些许不足与待优化的地方,如下图所示,若新集合的节点更新为:D、A、B、C,与老集合对比只有 D 节点移动,而 A、B、C 仍然保持原有的顺序,理论上 diff 应该只需对 D 执行移动操作,然而由于 D 在老集合的位置是最大的,导致其他节点的 _mountIndex < lastIndex,造成 D 没有执行移动操作,而是 A、B、C 全部移动到 D 节点后面的现象。 优化 react diff 算法 例如以下两个新旧数组,React的算***把 a, b, c 移动到他们的相应的位置 + 1共三步操作,而inferno.js则是直接将d移动到最前端这一步操作.其中一个核心的思想就是利用LIS(最长递增子序列)的思想做动态规划,找到最小的移动次数. * A: [a b c d] * B: [d a b c] React fiber 架构的理解 fiber 之前的 react存在的问题: 由于 js 引擎和页面渲染引擎两个线程是互斥的,当其中一个线程执行的时候,另一个线程只能挂起等待。 如果 js 线程长时间占用了主线程,那么我们渲染层面的更新就不得不长时间的等待,界面长时间不更新,会导致页面响应变慢,用户可能会感觉卡顿。 这也正是 react15 所面临的问题,当 react 在渲染组件时,从开始到渲染完成整个过程是一气呵成的,无法中断。 如果组件较大,那么 js 线程会一直执行,然后等到整棵 vDOM 树计算完成后,才会交给渲染线程,这就有可能出现卡顿的现象。 因此 facebook 提出了 react fiber架构 fiber 把渲染更新过程拆分为多个子任务,其中优先级高的先执行,并且每次只做其中的一小部分,做在完成一部分任务之后,将控制权交回给浏览器,让浏览器有时间进行页面的渲染。等浏览器忙完之后有剩余时间,再继续之前 React 未完成的任务,是一种合作式调度。 上述实现的方式是window.requestIdleCallback() 方法,该方法在浏览器空闲时段内调用排队的函数 会导致多次调用某些生命周期钩子 react把更新分为两个阶段 reconciliation 这个阶段的更新是可以被打断的,主要涉及的生命周期: componentWillMount componentWillReceiveProps shouldComponentUpdate componentWillUpdate commit 这个阶段的更新是不能被打断的 componentDidUpdate componentDidMount componentWillUnmount react 性能优化的手段 避免不必要的 render,主要是使用 shouldComponentUpdate 和 PureComponent 或者 Reac.memo 实现,pureComponent 只做浅层比较,因此可以使用 immutable 的方式进行更新 使用 useMemo 或 useCallback 缓存变量或者函数 使用 Suspense 或者 lazy 进行组件的懒加载 import React, { Suspense } from 'react';const OtherComponent = React.lazy(() => import('./OtherComponent'));function MyComponent() { return ( <div> <Suspense fallback={<div>Loading...</div>}> <OtherComponent /> </Suspense> </div> );} setState 是同步的还是异步的 主要取决于 react 当前是否处于 批量更新的模式,如果是,则会把更新存在一个队列里面,如果不是,则会直接执行此次更新。 setState 只在合成事件和钩子函数中是“异步”的,在原生事件和 setTimeout 中都是同步的。 setState的“异步”并不是说内部由异步代码实现,其实本身执行的过程和代码都是同步的,只是合成事件和钩子函数的调用顺序在更新之前,导致在合成事件和钩子函数中没法立马拿到更新后的值,形式了所谓的“异步”,当然可以通过第二个参数 setState(partialState, callback) 中的callback拿到更新后的结果。 setState 的批量更新优化也是建立在“异步”(合成事件、钩子函数)之上的,在原生事件和setTimeout 中不会批量更新,在“异步”中如果对同一个值进行多次 setState , setState 的批量更新策略会对其进行覆盖,取最后一次的执行,如果是同时 setState 多个不同的值,在更新时会对其进行合并批量更新。 React 合成事件 React 合成事件(SyntheticEvent)是 React 模拟原生 DOM 事件所有能力的一个事件对象。React 的合成事件它并不是绑定在 DOM 元素本身,而是统一绑定在 document 上。这样做主要有三个目的 进行浏览器兼容,实现更好的跨平台 事件对象可能会被频繁创建和回收,使用事件池来进行统一管理,达到性能优化的目的 合成事件与原生事件的执行顺序:先执行原生事件,再处理 React 合成事件 合成事件与原生事件的区别 命名方式不同 事件处理函数写法不同 阻止默认行为方式不同
点赞 25
评论 3
全部评论
推荐
最新
楼层
暂无评论,快来抢首评~
相关推荐
01-26 16:13
三环集团_后端开发工程师(准入职员工)
图拉斯内推,图拉斯内推码
面经:蓝禾的流程推得很快,基本上投完两天就接到了hr的初试电话,直接进行了初试。初试全程大概三十分钟,hr小哥态度很和善。主要问了实践经历获奖的情况最有成就感的事情对电商运营的理解选择公司的标准等,都是比较常规的问题。图拉斯2026届校招启动,今年HC翻倍,抓紧投递~【我们是】图拉斯(原蓝禾) 是一家集产品、设计、研发、品牌、营销和大数据运营于一体的创新型科技公司,总部位于中国深圳,全球员工规模超3000人。【base】深圳【岗位】运营(国内)、运营(国外)、营销、设计、研发技术、职能内推链接:https://lanhevip.jobs.feishu.cn/s/gAwh1MlZJsw内推码:H...
点赞
评论
收藏
分享
01-22 09:03
蚌埠坦克学院 嵌入式软件开发
快过年了看着自己的钱包陷入了迷茫
离过年还有一个月,日子却已经开始有了倒计时的味道。街上的年货悄悄上架,群里的聚会消息渐渐多了起来,而我低头看看自己的钱包,心里先一步过了个“清醒年”。一个月,说长不长,说短不短。足够让期待慢慢发酵,也足够让焦虑提前露面。工资、账单、车票、红包,在脑子里排着队登场,钱包却始终保持沉默,让人不免陷入一种微妙的迷茫。可迷茫归迷茫,日子还是要往前走。一个月,或许攒不出多少余裕,却能攒一份心态。少一点比较,多一点计划;少一点逞强,多一点从容。年不是比谁花得多,而是看谁过得稳。想到这里,钱包依旧不厚,心却慢慢踏实下来。还有一个月,足够整理生活,也足够好好迎接那个不必铺张、但依然温热的新年。
你最近因为什么迷茫?
点赞
评论
收藏
分享
不愿透露姓名的神秘牛友
2025-12-04 02:00
3个offer,纠结选哪一个
offer1:字节跳动(北京)- 后端开发岗- 薪资:总包42w(基本工资30w+绩效6w+年终奖6w),15薪,加班费按法定标准发放- 福利:公积金按12%缴纳,无宿舍,每月住房补贴2000元,餐补1500元,每年2次体检,免费健身房- 工作强度:996是常态,忙的时候可能到凌晨,团队节奏快,压力大- 其他:平台大,技术氛围浓,晋升路径清晰,对转行选手来说履历加分多,但北京生活成本高,租房压力大offer2:美团(上海)- 客户端开发岗- 薪资:总包38w(基本工资26w+绩效5w+年终奖7w),14薪,加班无加班费,可调休- 福利:公积金按10%缴纳,无宿舍,每月住房补贴1800元,餐补800元,每年1次体检,节日福利丰富- 工作强度:995为主,偶尔周末加班,项目紧急时会通宵,整体压力中等- 其他:公司业务成熟,行业地位稳固,客户端岗位需求稳定,上海生活节奏比北京稍缓,但租房成本仍较高offer3:网易(杭州)- 测试开发岗- 薪资:总包32w(基本工资22w+绩效4w+年终奖6w),13薪,加班较少,无加班费- 福利:公积金按12%缴纳,提供员工宿舍(单人间,前两年免费,第三年按市场价5折),每月餐补1000元,每年1次体检+1次旅游补贴- 工作强度:965为主,几乎无强制加班,团队氛围轻松,摸鱼文化盛行- 其他:杭州生活成本低于北上,宿舍省房租,测试开发岗入门难度低,适合转行过渡,但技术成长速度可能不如开发岗,未来跳槽竞争力未知本人情况:传统工科转行,编程基础一般,想快速提升技术能力,同时也希望工作生活能平衡,未来不确定是否留在一线城市。有没有同款转行选手或互联网前辈给点建议呀?
森七菜:
梦到什么说什么属于是
点赞
评论
收藏
分享
01-15 12:26
西南民族大学 Java
根本找不到实习 !甚至打了招呼都不回 而且还是没发简历的情况下
点赞
评论
收藏
分享
01-27 15:43
顺丰集团_HR(准入职员工)
顺丰内推,顺丰内推码
1月还有很多HC!!!!java笔试题目:用 Python 实现一个函数,用于计算斐波那契数列的第 n 项。以下 Java 代码片段是否存在问题?如果有,请指出并改正。简述 C++ 中指针和引用的区别。实现一个二叉树的中序遍历算法,可使用递归或非递归方式。对于一个无序整数数组,使用快速排序算法对其进行排序,并分析该算法的时间复杂度和空间复杂度。设计一个算法,判断一个字符串是否为回文串,要求时间复杂度尽可能低。简述 TCP 三次握手和四次挥手的过程,并说明为什么连接建立是三次握手,而连接释放是四次挥手。一台主机的 IP 地址为 192.168.1.100,子网掩码为 255.255.255.0,...
点赞
评论
收藏
分享
评论
点赞成功,聊一聊 >
点赞
收藏
分享
评论
提到的真题
返回内容
全站热榜
更多
1
...
J人永远闲不下来于是去提前实习
2204
2
...
mentor视角下的优秀实习生
2100
3
...
拥抱AI,程序员的最后出路
2027
4
...
牛客吐槽大会 | 有槽不吐,留着过年?吐完领现金红包,痛快!
1822
5
...
努力挣钱的意义具象化了
1674
6
...
真正会被取代的,是你心里面的幻觉
1399
7
...
大厂提前实习对AI开发的新感悟
1384
8
...
我身材再曼妙,也没有我的工资好笑!
1384
9
...
去独角兽做龙头还是去大厂做凤尾
1339
10
...
马斯克最新炸裂采访,AI会带走一半工作岗位,普通人将何去何从?
1234
创作者周榜
更多
正在热议
更多
#
牛客吐槽大会
#
3031次浏览
67人参与
#
机械人你知道哪些单休企业
#
83095次浏览
415人参与
#
今年春招是金一银二嘛?
#
8494次浏览
118人参与
#
参加完秋招的机械人,还参加春招吗?
#
103706次浏览
686人参与
#
1月小结:你过的开心吗?
#
1918次浏览
51人参与
#
抛开难度不谈,你最想去哪家公司?
#
4542次浏览
118人参与
#
为什么有人零实习也能进大厂?
#
5257次浏览
131人参与
#
AI求职实录
#
3842次浏览
108人参与
#
AI时代的工作 VS 传统时代的工作,有哪些不同?
#
8422次浏览
204人参与
#
机械人春招想让哪家公司来捞你?
#
379285次浏览
3141人参与
#
当你问AI“你会取代我的工作吗”,它说_?
#
3781次浏览
139人参与
#
你的第一家实习公司是什么档次?
#
4339次浏览
74人参与
#
没关系,至少我的__很曼妙
#
3751次浏览
65人参与
#
赚钱的意义在这一刻具象化
#
4043次浏览
99人参与
#
你的landing期是如何度过的?
#
8813次浏览
172人参与
#
除了Java,最推荐学什么技术?
#
5973次浏览
149人参与
#
我发现了面试通关密码
#
1600033次浏览
19679人参与
#
一人一道大厂面试题
#
114105次浏览
1263人参与
#
你觉得什么岗位会被AI替代
#
36819次浏览
256人参与
#
你在职场上见过哪些“水货”同事
#
30753次浏览
168人参与
牛客网
牛客网在线编程
牛客网题解
牛客企业服务