阅读 1699

18道 React 面试精选题含解答「面试高频」

前言

React 前端框架的受欢迎程度丝毫木有减弱的迹象,全国许多城市对开发人员仍供不应求。对于经验不足的开发人员(或那些已经失业了一段时间的开发人员),在面试阶段展示您的知识可能会令人生畏。

在本文中,我们将探讨18道面试必考题,这些问题涵盖了对理解和有效使用 React 技术至关重要的一系列知识。对于每个问题,我都会总结答案,并提供相关学习资源的链接,您可以在其中找到更多信息。

1. 什么是虚拟 DOM?

解答

虚拟 DOM 是组成应用程序用户界面的实际HTML元素的内存表示形式。重新渲染组件时,虚拟 DOM 会将更改与其 DOM 模型进行比较,以创建要应用的更新列表。主要优点是它高效,仅需对实际 DOM 进行最小限度的更改,而不必重新渲染大块。

进一步阅读

2. 什么是 JSX?

解答

JSX 是 JavaScript 语法的扩展,它允许编写类似于 HTML 的代码。它可以编译为常规的 JavaScript 函数调用,从而为创建组件标记提供了一种更好的方法。

JSX代码如下:

<div className="sidebar" />
复制代码

它转换为以下JS代码:

React.createElement(
  'div',
  {className: 'sidebar'}
)
复制代码

进一步阅读

3. 类组件和函数组件有何不同?

解答

在 React 16.8版本(引入钩子)之前,使用基于类的组件来创建需要维护内部状态或利用生命周期方法的组件(即componentDidMountshouldComponentUpdate)。基于类的组件是 ES6 类,它扩展了 React 的 Component 类,并且至少实现了render()方法。

类组件:

class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}
复制代码

函数组件是无状态的(同样,小于 React 16.8版本),并返回要呈现的输出。它们渲染 UI 的首选只依赖于属性,因为它们比基于类的组件更简单、更具性能。

函数组件:

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}
复制代码

注意:在 React 16.8版本中引入钩子意味着这些区别不再适用(请参阅14和15题)。

进一步阅读

4. React 中 keys 的作用是什么?

Keys 是 React 用于追踪哪些列表中元素被修改、被添加或者被移除的辅助标识。

在 React 中渲染集合时,向每个重复的元素添加关键字对于帮助React跟踪元素与数据之间的关联非常重要。key 应该是唯一ID,最好是 UUID 或收集项中的其他唯一字符串:

<ul>
  {todos.map((todo) =>
    <li key={todo.id}>
      {todo.text}
    </li>
  )};
</ul>
复制代码

在集合中添加和删除项目时,不使用键或将索引用作键会导致奇怪的行为。

进一步阅读

5. 状态(state)和属性(props)有何不同?

解答

props 是从父组件传递到组件的数据。它们不应该被改变,而应该只显示或用于计算其他值。状态是组件的内部数据,可以在组件的生存期内修改,并在重新呈现之间进行维护。

进一步阅读

6. 为什么调用 setState 而不是直接改变 state?

解答

如果您尝试直接改变组件的状态,React 将无法得知它需要重新渲染组件。通过使用setState()方法,React 可以更新组件的UI。

另外,您还可以谈谈如何不保证状态更新是同步的。如果需要基于另一个状态(或属性)更新组件的状态,请向setState()传递一个函数,该函数将 state 和 props 作为其两个参数:

this.setState((state, props) => ({
  counter: state.counter + props.increment
}));
复制代码

进一步阅读

7. 如何限制作为 props 传递的值的类型,或使其成为必需的?

解答

为了对组件的 props 进行类型检查,您可以使用prop-types包(以前作为React的一部分包含在15.5版本之前)来声明期望的值类型以及是否需要该prop:

import PropTypes from 'prop-types';

class Greeting extends React.Component {
  render() {
    return (
      <h1>Hello, {this.props.name}</h1>
    );
  }
}

Greeting.propTypes = {
  name: PropTypes.string
};
复制代码

进一步阅读

8. 什么是 prop drilling,如何避免它?

Prop drilling(也叫线程)是当您需要将数据从父组件向下传递到层次结构中较低的组件时所发生的事情,“drilling”通过除了传递它们之外不需要 props 本身的其他组件。

有时,可以通过重构组件,避免过早将组件分解为较小的组件以及将公共状态保持在最接近的公共父级中来避免进行钻探。如果您需要在组件树中深处/彼此之间的组件之间共享状态,则可以使用 React 的 Context API 或专用的状态管理库(例如 Redux)。

进一步阅读

9. React context 是什么?

解答

React 提供了上下文API,以解决应用内多个组件之间共享状态的问题。在引入上下文之前,唯一的选择是引入一个单独的状态管理库,例如 Redux。但是,许多开发人员认为 Redux 引入了许多不必要的复杂性,尤其是对于较小的应用程序。

进一步阅读

10. Redux 是什么?

解答

Redux 是 React 的第三方状态管理库,创建于上下文API存在之前。它基于一个称为存储的状态容器的概念,组件可以从该容器中作为 props 接收数据。更新存储区的唯一方法是向存储区发送一个操作,该操作被传递到一个reducer中。reducer接收操作和当前状态,并返回一个新状态,触发订阅的组件重新渲染。

进一步阅读

11. 样式化 React 应用程序的最常见方法是什么?

解答

有多种方法可用于对 React 组件进行样式设置,每种方法各有利弊。要提到的主要是:

  • 内联样式:适用于原型设计,但有局限性(例如,无伪类样式)
  • 基于类的CSS样式:比内联样式更高效,并且对 React 刚接触的开发人员来说是熟悉的
  • JS样式中的CSS:有许多库允许将样式在组件内声明为 JavaScript,从而将样式更像代码。

进一步阅读

12. 受控组件和非受控组件有什么区别?

解答

在 HTML 文档中,许多表单元素(例如<select>、<textrarea>、<input>)都保持自己的状态。不受控制的组件将 DOM 视为这些输入状态的真实源。在受控组件中,内部状态用于跟踪元素值。当输入值改变时,React 会重新渲染输入。

在与非 React 代码集成时,不受控制的组件非常有用(例如,如果您需要支持某种 jQuery 表单插件)。

进一步阅读

13. 生命周期方法是什么?

解答

基于类的组件可以声明在其生命周期中某些特定时刻调用的特殊方法,例如何时将其装入(渲染到 DOM 中)以及何时将其卸载。例如,它们对于设置和拆除组件可能需要的东西,设置计时器或绑定到浏览器事件很有用。

可以在组件中实现以下生命周期方法:

  • componentWillMount:在创建组件之后但在将其渲染到DOM中之前调用
  • componentDidMount:在第一个渲染之后调用;组件的DOM元素现在可用
  • componentWillReceiveProps:当属性更新时调用
  • shouldComponentUpdate:当收到新props时,此方法可以防止重新渲染以优化性能
  • componentWillUpdate:在收到新的props并且ComponentUpdate返回true时调用
  • componentDidUpdate:在组件更新后调用
  • componentWillUnmount:在组件从DOM中移除之前调用,允许您清理事件侦听器之类的内容。

在处理函数式组件时,可以使用 useEffect 钩子来复制生命周期行为。

进一步阅读

14. React hooks 是什么?

解答

Hooks 钩子是 React 试图将基于类的组件(即内部状态和生命周期方法)的优势引入函数式组件的尝试。

进一步阅读

15. React hooks 有什么优势?

解答

向 React 引入钩子有几个明显的好处:

  • 不需要基于类的组件、生命周期钩子和 this 关键字
  • 通过将公共函数抽象到定制钩子中,使重用逻辑变得更容易
  • 通过能够将逻辑与组件本身分离出来,使代码更具可读性和可测试性

进一步阅读

16. 为什么虚拟 dom 会提高性能?

解答

  • 虚拟 dom 相当于在 js 和真实 dom 中间加了一个缓存,利用 dom diff 算法避免了没有必要的 dom 操作,从而提高性能。
  • 用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异把 2 所记录的差异应用到步骤 1 所构建的真正的 DOM 树上,视图就更新了。

进一步阅读

17. React diff 原理

解答

  • 把树形结构按照层级分解,只比较同级元素。
  • 列表结构的每个单元添加唯一的 key 属性,方便比较。
  • React 只会匹配相同 class 的 component(这里面的 class 指的是组件的名字)
  • 合并操作,调用 component 的 setState 方法的时候, React 将其标记为 dirty 到每一个事件循环结束, React 检查所有标记 dirty 的 component 重新绘制.
  • 选择性子树渲染。开发人员可以重写 shouldComponentUpdate 提高 diff 的性能。

进一步阅读

18. React 中 refs 的作用是什么?

解答

Refs 是 React 提供给我们的安全访问 DOM 元素或者某个组件实例的句柄。

我们可以为元素添加 ref 属性然后在回调函数中接受该元素在 DOM 树中的句柄,该值会作为回调函数的第一个参数返回:

class CustomForm extends Component {
  handleSubmit = () => {
    console.log("Input Value: ", this.input.value)
  }
  render () {
    return (
      <form onSubmit={this.handleSubmit}>
        <input
          type='text'
          ref={(input) => this.input = input} />
        <button type='submit'>Submit</button>
      </form>
    )
  }
}
复制代码

上述代码中的 input 域包含了一个 ref 属性,该属性声明的回调函数会接收 input 对应的 DOM 元素,我们将其绑定到 this 指针以便在其他的类函数中使用。

另外值得一提的是,refs 并不是类组件的专属,函数式组件同样能够利用闭包暂存其值:

function CustomForm ({handleSubmit}) {
  let inputElement
  return (
    <form onSubmit={() => handleSubmit(inputElement.value)}>
      <input
        type='text'
        ref={(input) => inputElement = input} />
      <button type='submit'>Submit</button>
    </form>
  )
}
复制代码

进一步阅读

总结

虽然这并不是一个全面的清单(React 是不断发展的),但这些问题涉及很多方面。了解这些问题将使您对库及其最新的一些更改有很好的了解。建议进一步阅读将有助于巩固你的理解能力,这样你就能展示由浅入深的知识。

希望能对 React 初学者或者面试者有所帮助,祝你好运!

参考资料:15 React Interview Questions with Solutions

喜欢可以关注作者公众号【懒人码农】,我们一起学习一起进步。。。