写点什么

2021 前端 react 面试题汇总

用户头像
it优课
关注
发布于: 39 分钟前

2021 前端 react 面试题汇总

React 视频教程系列

React 实战:CNode 视频教程

完整教程目录:点击查看

React 经典教程-从入门到精通

完整教程目录:点击查看

最新最全前端毕设项目(小程序+VUE+Noed+React+uni app+Express+Mongodb)

完整教程目录:点击查看

2021 前端 React 精品教程

完整教程目录:点击查看

1. mobox 和 redux 有什么区别?

(1)共同点

  • 为了解决状态管理混乱,无法有效同步的问题统一维护管理应用状态;

  • 某一状态只有一个可信数据来源(通常命名为 store,指状态容器);

  • 操作更新状态方式统一,并且可控(通常以 action 方式提供更新状态的途径);

  • 支持将 store 与 React 组件连接,如 react-redux,mobx- react;

(2)区别 Redux 更多的是遵循 Flux 模式的一种实现,是一个 JavaScript 库,它关注点主要是以下几方面∶

  • Action∶ 一个 JavaScript 对象,描述动作相关信息,主要包含 type 属性和 payload 属性∶

    o type∶ action 类型; o payload∶ 负载数据; 复制代码

  • Reducer∶ 定义应用状态如何响应不同动作(action),如何更新状态;

  • Store∶ 管理 action 和 reducer 及其关系的对象,主要提供以下功能∶

    o 维护应用状态并支持访问状态(getState()); o 支持监听action的分发,更新状态(dispatch(action)); o 支持订阅store的变更(subscribe(listener)); 复制代码

  • 异步流∶ 由于 Redux 所有对 store 状态的变更,都应该通过 action 触发,异步任务(通常都是业务或获取数据任务)也不例外,而为了不将业务或数据相关的任务混入 React 组件中,就需要使用其他框架配合管理异步任务流程,如 redux-thunk,redux-saga 等;

Mobx 是一个透明函数响应式编程的状态管理库,它使得状态管理简单可伸缩∶

  • Action∶定义改变状态的动作函数,包括如何变更状态;

  • Store∶ 集中管理模块状态(State)和动作(action)

  • Derivation(衍生)∶ 从应用状态中派生而出,且没有任何其他影响的数据

对比总结:

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

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

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

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

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

2. Redux 和 Vuex 有什么区别,它们的共同思想

(1)Redux 和 Vuex 区别

  • Vuex 改进了 Redux 中的 Action 和 Reducer 函数,以 mutations 变化函数取代 Reducer,无需 switch,只需在对应的 mutation 函数里改变 state 值即可

  • Vuex 由于 Vue 自动重新渲染的特性,无需订阅重新渲染函数,只要生成新的 State 即可

  • Vuex 数据流的顺序是∶View 调用 store.commit 提交对应的请求到 Store 中对应的 mutation 函数->store 改变(vue 检测到数据变化自动渲染)

通俗点理解就是,vuex 弱化 dispatch,通过 commit 进行 store 状态的一次更变;取消了 action 概念,不必传入特定的 action 形式进行指定变更;弱化 reducer,基于 commit 参数直接对数据进行转变,使得框架更加简易;

(2)共同思想

  • 单—的数据源

  • 变化可以预测

本质上∶ redux 与 vuex 都是对 mvvm 思想的服务,将数据从视图中抽离的一种方案。

3. Redux 中间件是怎么拿到 store 和 action? 然后怎么处理?

redux 中间件本质就是一个函数柯里化。redux applyMiddleware Api 源码中每个 middleware 接受 2 个参数, Store 的 getState 函数和 dispatch 函数,分别获得 store 和 action,最终返回一个函数。该函数会被传入 next 的下一个 middleware 的 dispatch 方法,并返回一个接收 action 的新函数,这个函数可以直接调用 next(action),或者在其他需要的时刻调用,甚至根本不去调用它。调用链中最后一个 middleware 会接受真实的 store 的 dispatch 方法作为 next 参数,并借此结束调用链。所以,middleware 的函数签名是({ getState,dispatch })=> next => action。

4. Redux 中的 connect 有什么作用

connect 负责连接 React 和 Redux

(1)获取 state

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

(2)包装原组件

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

(3)监听 store tree 变化

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

5. React Hooks 解决了哪些问题?

React Hooks 主要解决了以下问题:

(1)在组件之间复用状态逻辑很难

React 没有提供将可复用性行为“附加”到组件的途径(例如,把组件连接到 store)解决此类问题可以使用 render props 和 高阶组件。但是这类方案需要重新组织组件结构,这可能会很麻烦,并且会使代码难以理解。由 providers,consumers,高阶组件,render props 等其他抽象层组成的组件会形成“嵌套地狱”。尽管可以在 DevTools 过滤掉它们,但这说明了一个更深层次的问题:React 需要为共享状态逻辑提供更好的原生途径。

可以使用 Hook 从组件中提取状态逻辑,使得这些逻辑可以单独测试并复用。Hook 使我们在无需修改组件结构的情况下复用状态逻辑。 这使得在组件间或社区内共享 Hook 变得更便捷。

(2)复杂组件变得难以理解

在组件中,每个生命周期常常包含一些不相关的逻辑。例如,组件常常在 componentDidMount 和 componentDidUpdate 中获取数据。但是,同一个 componentDidMount 中可能也包含很多其它的逻辑,如设置事件监听,而之后需在 componentWillUnmount 中清除。相互关联且需要对照修改的代码被进行了拆分,而完全不相关的代码却在同一个方法中组合在一起。如此很容易产生 bug,并且导致逻辑不一致。

在多数情况下,不可能将组件拆分为更小的粒度,因为状态逻辑无处不在。这也给测试带来了一定挑战。同时,这也是很多人将 React 与状态管理库结合使用的原因之一。但是,这往往会引入了很多抽象概念,需要你在不同的文件之间来回切换,使得复用变得更加困难。

为了解决这个问题,Hook 将组件中相互关联的部分拆分成更小的函数(比如设置订阅或请求数据),而并非强制按照生命周期划分。你还可以使用 reducer 来管理组件的内部状态,使其更加可预测。

(3)难以理解的 class

除了代码复用和代码管理会遇到困难外,class 是学习 React 的一大屏障。我们必须去理解 JavaScript 中 this 的工作方式,这与其他语言存在巨大差异。还不能忘记绑定事件处理器。没有稳定的语法提案,这些代码非常冗余。大家可以很好地理解 props,state 和自顶向下的数据流,但对 class 却一筹莫展。即便在有经验的 React 开发者之间,对于函数组件与 class 组件的差异也存在分歧,甚至还要区分两种组件的使用场景。

为了解决这些问题,Hook 使你在非 class 的情况下可以使用更多的 React 特性。 从概念上讲,React 组件一直更像是函数。而 Hook 则拥抱了函数,同时也没有牺牲 React 的精神原则。Hook 提供了问题的解决方案,无需学习复杂的函数式或响应式编程技术

6. 为什么 React 并不推荐优先考虑使用 Context?

  • Context 目前还处于实验阶段,可能会在后面的发行版本中有很大的变化,事实上这种情况已经发生了,所以为了避免给今后升级带来大的影响和麻烦,不建议在 app 中使用 context。

  • 尽管不建议在 app 中使用 context,但是独有组件而言,由于影响范围小于 app,如果可以做到高内聚,不破坏组件树之间的依赖关系,可以考虑使用 context

  • 对于组件之间的数据通信或者状态管理,有效使用 props 或者 state 解决,然后再考虑使用第三方的成熟库进行解决,以上的方法都不是最佳的方案的时候,在考虑 context。

  • context 的更新需要通过 setState()触发,但是这并不是很可靠的,Context 支持跨组件的访问,但是如果中间的子组件通过一些方法不影响更新,比如 shouldComponentUpdate() 返回 false 那么不能保证 Context 的更新一定可以使用 Context 的子组件,因此,Context 的可靠性需要关注

7. React 中什么是受控组件和非控组件?

(1)受控组件 在使用表单来收集用户输入时,例如<input><select><textearea>等元素都要绑定一个 change 事件,当表单的状态发生变化,就会触发 onChange 事件,更新组件的 state。这种组件在 React 中被称为受控组件,在受控组件中,组件渲染出的状态与它的 value 或 checked 属性相对应,react 通过这种方式消除了组件的局部状态,使整个状态可控。react 官方推荐使用受控表单组件。

受控组件更新 state 的流程:

  • 可以通过初始 state 中设置表单的默认值

  • 每当表单的值发生变化时,调用 onChange 事件处理器

  • 事件处理器通过事件对象 e 拿到改变后的状态,并更新组件的 state

  • 一旦通过 setState 方法更新 state,就会触发视图的重新渲染,完成表单组件的更新

受控组件缺陷: 表单元素的值都是由 React 组件进行管理,当有多个输入框,或者多个这种组件时,如果想同时获取到全部的值就必须每个都要编写事件处理函数,这会让代码看着很臃肿,所以为了解决这种情况,出现了非受控组件。

(2)非受控组件 如果一个表单组件没有 value props(单选和复选按钮对应的是 checked props)时,就可以称为非受控组件。在非受控组件中,可以使用一个 ref 来从 DOM 获得表单值。而不是为每个状态更新编写一个事件处理程序。

React 官方的解释:

要编写一个非受控组件,而不是为每个状态更新都编写数据处理函数,你可以使用 ref 来从 DOM 节点中获取表单数据。 因为非受控组件将真实数据储存在 DOM 节点中,所以在使用非受控组件时,有时候反而更容易同时集成 React 和非 React 代码。如果你不介意代码美观性,并且希望快速编写代码,使用非受控组件往往可以减少你的代码量。否则,你应该使用受控组件。

例如,下面的代码在非受控组件中接收单个属性:

class NameForm extends React.Component {  constructor(props) {    super(props);    this.handleSubmit = this.handleSubmit.bind(this);  }  handleSubmit(event) {    alert('A name was submitted: ' + this.input.value);    event.preventDefault();  }  render() {    return (      <form onSubmit={this.handleSubmit}>        <label>          Name:          <input type="text" ref={(input) => this.input = input} />        </label>        <input type="submit" value="Submit" />      </form>    );  }}复制代码
复制代码

总结: 页面中所有输入类的 DOM 如果是现用现取的称为非受控组件,而通过 setState 将输入的值维护到了 state 中,需要时再从 state 中取出,这里的数据就受到了 state 的控制,称为受控组件。

8. React 中 refs 的作用是什么?有哪些应用场景?

Refs 提供了一种方式,用于访问在 render 方法中创建的 React 元素或 DOM 节点。Refs 应该谨慎使用,如下场景使用 Refs 比较适合:

  • 处理焦点、文本选择或者媒体的控制

  • 触发必要的动画

  • 集成第三方 DOM 库

Refs 是使用 React.createRef() 方法创建的,他通过 ref 属性附加到 React 元素上。要在整个组件中使用 Refs,需要将 ref 在构造函数中分配给其实例属性:

class MyComponent extends React.Component {  constructor(props) {    super(props)    this.myRef = React.createRef()  }  render() {    return <div ref={this.myRef} />  }}复制代码
复制代码

由于函数组件没有实例,因此不能在函数组件上直接使用 ref

function MyFunctionalComponent() {  return <input />;}class Parent extends React.Component {  constructor(props) {    super(props);    this.textInput = React.createRef();  }  render() {    // 这将不会工作!    return (      <MyFunctionalComponent ref={this.textInput} />    );  }}复制代码
复制代码

但可以通过闭合的帮助在函数组件内部进行使用 Refs:

function CustomTextInput(props) {  // 这里必须声明 textInput,这样 ref 回调才可以引用它  let textInput = null;  function handleClick() {    textInput.focus();  }  return (    <div>      <input        type="text"        ref={(input) => { textInput = input; }} />      <input        type="button"        value="Focus the text input"        onClick={handleClick}      />    </div>  );  }复制代码
复制代码

注意:

  • 不应该过度的使用 Refs

  • ref

    的返回值取决于节点的类型:

    当 ref 属性被用于一个普通的 HTML 元素时,React.createRef() 将接收底层 DOM 元素作为他的 current 属性以创建 ref

    当 ref 属性被用于一个自定义的类组件时,ref 对象将接收该组件已挂载的实例作为他的 current

  • 当在父组件中需要访问子组件中的 ref 时可使用传递 Refs 或回调 Refs。

9. React 组件的构造函数有什么作用?它是必须的吗?

构造函数主要用于两个目的:

  • 通过将对象分配给 this.state 来初始化本地状态

  • 将事件处理程序方法绑定到实例上

所以,当在 React class 中需要设置 state 的初始值或者绑定事件时,需要加上构造函数,官方 Demo:

class LikeButton extends React.Component {  constructor() {    super();    this.state = {      liked: false    };    this.handleClick = this.handleClick.bind(this);  }  handleClick() {    this.setState({liked: !this.state.liked});  }  render() {    const text = this.state.liked ? 'liked' : 'haven\'t liked';    return (      <div onClick={this.handleClick}>        You {text} this. Click to toggle.      </div>    );  }}ReactDOM.render(  <LikeButton />,  document.getElementById('example'));复制代码
复制代码

构造函数用来新建父类的 this 对象;子类必须在 constructor 方法中调用 super 方法;否则新建实例时会报错;因为子类没有自己的 this 对象,而是继承父类的 this 对象,然后对其进行加工。如果不调用 super 方法;子类就得不到 this 对象。

注意:

  • constructor () 必须配上 super(), 如果要在 constructor 内部使用 this.props 就要 传入 props , 否则不用

  • JavaScript 中的 bind 每次都会返回一个新的函数, 为了性能等考虑, 尽量在 constructor 中绑定事件

10. React.forwardRef 是什么?它有什么作用?

React.forwardRef 会创建一个 React 组件,这个组件能够将其接受的 ref 属性转发到其组件树下的另一个组件中。这种技术并不常见,但在以下两种场景中特别有用:

  • 转发 refs 到 DOM 组件

  • 在高阶组件中转发 refs

用户头像

it优课

关注

还未添加个人签名 2021.06.26 加入

还未添加个人简介

评论

发布
暂无评论
2021前端react面试题汇总