写点什么

11 道高频 React 面试题及详解, 另附有 React 面试题集合

用户头像
前端依依
关注
发布于: 2021 年 07 月 20 日

为什么选择使用框架而不是原生?

框架的好处:


1.组件化: 其中以 React 的组件化最为彻底,甚至可以到函数级别的原子组件,高度的组件化可以是我们的工程易于维护、易于组合拓展。


2.天然分层: JQuery`时代的代码大部分情况下是面条代码,耦合严重,现代框架不管是 MVC、MVP 还是 MVVM 模式都能帮助我们进行分层,代码解耦更易于读写。


3.生态: 现在主流前端框架都自带生态,不管是数据流管理架构还是 UI 库都有成熟的解决方案。


4.开发效率: 现代前端框架都默认自动更新 DOM,而非我们手动操作,解放了开发者的手动 DOM 成本,提高开发效率,从根本上解决了 UI 与状态同步问题.

虚拟 DOM 的优劣如何?

优点:


  • 保证性能下限: 虚拟 DOM 可以经过 diff 找出最小差异,然后批量进行 patch,这种操作虽然比不上手动优化,但是比起粗暴的 DOM 操作性能要好很多,因此虚拟 DOM 可以保证性能下限

  • 无需手动操作 DOM: 虚拟 DOM 的 diff 和 patch 都是在一次更新中自动进行的,我们无需手动操作 DOM,极大提高开发效率

  • 跨平台: 虚拟 DOM 本质上是 JavaScript 对象,而 DOM 与平台强相关,相比之下虚拟 DOM 可以进行更方便地跨平台操作,例如服务器渲染、移动端开发等等


缺点:


  • 无法进行极致优化: 在一些性能要求极高的应用中虚拟 DOM 无法进行针对性的极致优化,比如 VScode 采用直接手动操作 DOM 的方式进行极端的性能优化

虚拟 DOM 实现原理?

  • 虚拟 DOM 本质上是 JavaScript 对象,是对真实 DOM 的抽象

  • 状态变更时,记录新树和旧树的差异

  • 最后把差异更新到真正的 dom 中

React 的请求应该放在哪个生命周期中?

React 的异步请求到底应该放在哪个生命周期里,有人认为在 componentWillMount 中可以提前进行异步请求,避免白屏,其实这个观点是有问题的.


由于 JavaScript 中异步事件的性质,当您启动 API 调用时,浏览器会在此期间返回执行其他工作。当 React 渲染一个组件时,它不会等待 componentWillMount 它完成任何事情 - React 继续前进并继续 render,没有办法“暂停”渲染以等待数据到达。


而且在 componentWillMount 请求会有一系列潜在的问题,首先,在服务器渲染时,如果在 componentWillMount 里获取数据,fetch data 会执行两次,一次在服务端一次在客户端,这造成了多余的请求,其次,在 React 16 进行 React Fiber 重写后,componentWillMount 可能在一次渲染中多次调用.


目前官方推荐的异步请求是在 componentDidmount 中进行.


如果有特殊需求需要提前请求,也可以在特殊情况下在 constructor 中请求:

setState 到底是异步还是同步?

先给出答案: 有时表现出异步,有时表现出同步


1.setState 只在合成事件和钩子函数中是“异步”的,在原生事件和 setTimeout 中都是同步的。


2.setState 的“异步”并不是说内部由异步代码实现,其实本身执行的过程和代码都是同步的,只是合成事件和钩子函数的调用顺序在更新之前,导致在合成事件和钩子函数中没法立马拿到更新后的值,形成了所谓的“异步”,当然可以通过第二个参数 setState(partialState, callback) 中的 callback 拿到更新后的结果。


3.setState 的批量更新优化也是建立在“异步”(合成事件、钩子函数)之上的,在原生事件和 setTimeout 中不会批量更新,在“异步”中如果对同一个值进行多次 setState,setState 的批量更新策略会对其进行覆盖,取最后一次的执行,如果是同时 setState 多个不同的值,在更新时会对其进行合并批量更新。

React 组件通信如何实现?

React 组件间通信方式:


  • 父组件向子组件通讯: 父组件可以向子组件通过传 props 的方式,向子组件进行通讯

  • 子组件向父组件通讯: props+回调的方式,父组件向子组件传递 props 进行通讯,此 props 为作用域为父组件自身的函数,子组件调用该函数,将子组件想要传递的信息,作为参数,传递到父组件的作用域中

  • 兄弟组件通信: 找到这两个兄弟节点共同的父节点,结合上面两种方式由父节点转发信息进行通信

  • 跨层级通信: Context 设计目的是为了共享那些对于一个组件树而言是“全局”的数据,例如当前认证的用户、主题或首选语言,对于跨越多层的全局数据通过 Context 通信再适合不过

  • 发布订阅模式: 发布者发布事件,订阅者监听事件并做出反应,我们可以通过引入 event 模块进行通信- 全局状态管理工具: 借助 Redux 或者 Mobx 等全局状态管理工具进行通信,这种工具会维护一个全局状态中心 Store,并根据不同的事件产生新的状态


redux 的工作流程?

首先,我们看下几个核心概念:


  • Store: 保存数据的地方,你可以把它看成一个容器,整个应用只能有一个 Store。

  • State: Store 对象包含所有数据,如果想得到某个时点的数据,就要对 Store 生成快照,这种时点的数据集合,就叫做 State。

  • Action: State 的变化,会导致 View 的变化。但是,用户接触不到 State,只能接触到 View。所以,State 的变化必须是 View 导致的。Action 就是 View 发出的通知,表示 State 应该要发生变化了。

  • Action Creator: View 要发送多少种消息,就会有多少种 Action。如果都手写,会很麻烦,所以我们定义一个函数来生成 Action,这个函数就叫 Action Creator。

  • Reducer: Store 收到 Action 以后,必须给出一个新的 State,这样 View 才会发生变化。这种 State 的计算过程就叫做 Reducer。Reducer 是一个函数,它接受 Action 和当前 State 作为参数,返回一个新的 State。

  • dispatch: 是 View 发出 Action 的唯一方法。


然后我们过下整个工作流程:1.首先,用户(通过 View)发出 Action,发出方式就用到了 dispatch 方法。


2.然后,Store 自动调用 Reducer,并且传入两个参数:当前 State 和收到的 Action,Reducer 会返回新的 State


3.State 一旦有变化,Store 就会调用监听函数,来更新 View。


到这儿为止,一次用户交互流程结束。可以看到,在整个流程中数据都是单向流动的,这种方式保证了流程的清晰。



react-redux 是如何工作的?

  • Provider: Provider 的作用是从最外部封装了整个应用,并向 connect 模块传递 store

  • connect: 负责连接 React 和 Redux


获取 state: connect 通过 context 获取 Provider 中的 store,通过 store.getState()获取整个 store tree 上所有 state


包装原组件: 将 state 和 action 通过 props 的方式传入到原组件内部 wrapWithConnect 返回一个 ReactComponent 对象 Connect,Connect 重新 render 外部传入的原组件 WrappedComponent,并把 connect 中传入的 mapStateToProps, mapDispatchToProps 与组件上原有的 props 合并后,通过属性的方式传给 WrappedComponent


监听 store tree 变化: connect 缓存了 store tree 中 state 的状态,通过当前 state 状态和变更前 state 状态进行比较,从而确定是否调用 this.setState()方法触发 Connect 及其子组件的重新渲染



redux 与 mobx 的区别?

两者对比:


  • redux 将数据保存在单一的 store 中,mobx 将数据保存在分散的多个 store 中

  • redux 使用 plain object 保存数据,需要手动处理变化后的操作;mobx 适用 observable 保存数据,数据变化后自动处理响应的操作

  • redux 使用不可变状态,这意味着状态是只读的,不能直接去修改它,而是应该返回一个新的状态,同时使用纯函数;- - - - mobx 中的状态是可变的,可以直接对其进行修改

  • mobx 相对来说比较简单,在其中有很多的抽象,mobx 更多的使用面向对象的编程思维;redux 会比较复杂,因为其中的函数式编程思想掌握起来不是那么容易,同时需要借助一系列的中间件来处理异步和副作用

  • mobx 中有更多的抽象和封装,调试会比较困难,同时结果也难以预测;而 redux 提供能够进行时间回溯的开发工具,同时其纯函数以及更少的抽象,让调试变得更加的容易


场景辨析:基于以上区别,我们可以简单得分析一下两者的不同使用场景.


mobx 更适合数据不复杂的应用: mobx 难以调试,很多状态无法回溯,面对复杂度高的应用时,往往力不从心.


redux 适合有回溯需求的应用: 比如一个画板应用、一个表格应用,很多时候需要撤销、重做等操作,由于 redux 不可变的特性,天然支持这些操作.


mobx 适合短平快的项目: mobx 上手简单,样板代码少,可以很大程度上提高开发效率.当然 mobx 和 redux 也并不一定是非此即彼的关系,你也可以在项目中用 redux 作为全局状态管理,用 mobx 作为组件局部状态管理器来用.

redux 中如何进行异步操作?

当然,我们可以在`componentDidmount 中直接进行请求无须借助 redux.


但是在一定规模的项目中,上述方法很难进行异步流的管理,通常情况下我们会借助 redux 的异步中间件进行异步处理.


redux 异步流中间件其实有很多,但是当下主流的异步中间件只有两种 redux-thunk、redux-saga,当然 redux-observable 可能也有资格占据一席之地,其余的异步中间件不管是社区活跃度还是 npm 下载量都比较差了。

redux 异步中间件之间的优劣?

redux-thunk 优点:


  • 体积小: redux-thunk 的实现方式很简单,只有不到 20 行代码

  • 使用简单: redux-thunk 没有引入像 redux-saga 或者 redux-observable 额外的范式,上手简单


redux-thunk 缺陷:


  • 样板代码过多: 与 redux 本身一样,通常一个请求需要大量的代码,而且很多都是重复性质的

  • 耦合严重: 异步操作与 redux 的 action 偶合在一起,不方便管理

  • 功能孱弱: 有一些实际开发中常用的功能需要自己进行封装


redux-saga 优点:


  • 异步解耦: 异步操作被被转移到单独 saga.js 中,不再是掺杂在 action.js 或 component.js 中 action 摆脱 thunk function: dispatch 的参数依然是一个纯粹的 action (FSA),而不是充满 “黑魔法” thunk function

  • 异常处理: 受益于 generator function 的 saga 实现,代码异常/请求失败 都可以直接通过 try/catch 语法直接捕获处理

  • 功能强大: redux-saga 提供了大量的 Saga 辅助函数和 Effect 创建器供开发者使用,开发者无须封装或者简单封装即可使用

  • 灵活: redux-saga 可以将多个 Saga 可以串行/并行组合起来,形成一个非常实用的异步 flow 易测试,提供了各种 case 的测试方案,包括 mock task,分支覆盖等等


redux-saga 缺陷:


  • 额外的学习成本: redux-saga 不仅在使用难以理解的 generator function,而且有数十个 API,学习成本远超 redux-thunk,最重要的是你的额外学习成本是只服务于这个库的,与 redux-observable 不同,redux-observable 虽然也有额外学习成本但是背后是 rxjs 和一整套思想

  • 体积庞大: 体积略大,代码近 2000 行,min 版 25KB 左右

  • 功能过剩: 实际上并发控制等功能很难用到,但是我们依然需要引入这些代码

  • ts 支持不友好: yield 无法返回 TS 类型


redux-observable 优点:


  • 功能最强: 由于背靠 rxjs 这个强大的响应式编程的库,借助 rxjs 的操作符,你可以几乎做任何你能想到的异步处理

  • 背靠 rxjs: 由于有 rxjs 的加持,如果你已经学习了 rxjs,redux-observable 的学习成本并不高,而且随着 rxjs 的升级 redux-observable 也会变得更强大


redux-observable 缺陷:


  • 学习成本奇高: 如果你不会 rxjs,则需要额外学习两个复杂的库

  • 社区一般: redux-observable 的下载量只有 redux-saga 的 1/5,社区也不够活跃,在复杂异步流中间件这个层面 redux-saga 仍处于领导地位

React 面试题集合

面试光看这 11 道题怎么够呢?小编把 React 面试题整理成了一个集合,不多但经典哦。

基本知识

  • 区分 Real DOM 和 Virtual DOM

  • 什么是 React?

  • React 有什么特点?

  • 列出 React 的一些主要优点。

  • React 有哪些限制?

  • 什么是 JSX?###React 组件

  • 你理解“在 React 中,一切都是组件”这句话。

  • 解释 React 中 render() 的目的。

  • 如何将两个或多个组件嵌入到一个组件中?

  • 什么是 Props?

  • React 中的状态是什么?它是如何使用的?

  • 区分状态和 props

React Redux

  • MVC 框架的主要问题是什么?

  • 解释一下 Flux

  • 什么是 Redux?

  • Redux 遵循的三个原则是什么?

  • 你对“单一事实来源”有什么理解?

  • 列出 Redux 的组件。

React 路由

  • 什么是 React 路由?

  • 为什么 React Router v4 中使用 switch 关键字 ?

  • 为什么需要 React 中的路由?

  • 列出 React Router 的优点。

  • React Router 与常规路由有何不同?




完整版的 React 面试题集合 PDF 资料直接点击这里就可以领取了哦。整理不易,还请点赞评论支持小编,给小编充充能量,谢谢!

用户头像

前端依依

关注

在下是只会cv大法的前端媛! 2021.07.06 加入

这里会分享前端技术、知识、面试等内容,一起学习一起成长!

评论

发布
暂无评论
11道高频React面试题及详解,另附有React面试题集合