react面试题笔记整理

2022-11-04 07:46:34 浏览数 (3)

componentWillReceiveProps调用时机

  • 已经被废弃掉
  • 当props改变的时候才调用,子组件第二次接收到props的时候

在调用setState 之后发生了什么

  • 状态合并,触发调和:

setState函数之后,会将传入的参数对象与当前的状态合并,然后出发调用过程

  • 根据新的状态构建虚拟dom树

经过调和过程,react会高效的根据新的状态构建虚拟DOM树,准备渲染整个UI页面

  • 计算新老树节点差异,最小化渲染

得倒新的虚拟DOM树后,会计算出新老树的节点差异,会根据差异对界面进行最小化渲染

  • 按需更新

在差异话计算中,react可以相对准确的知道哪些位置发生了改变以及该如何改变,这保证按需更新,而不是宣布重新渲染

概述一下 React中的事件处理逻辑。

为了解决跨浏览器兼容性问题, React会将浏览器原生事件( Browser Native Event)封装为合成事件( Synthetic Event)并传入设置的事件处理程序中。

这里的合成事件提供了与原生事件相同的接口,不过它们屏蔽了底层浏览器的细节差异,保证了行为的一致性。另外, React并没有直接将事件附着到子元素上,而是以单一事件监听器的方式将所有的事件发送到顶层进行处理(基于事件委托原理)。

这样 React在更新DOM时就不需要考虑如何处理附着在DOM上的事件监听器,最终达到优化性能的目的。

在 React中元素( element)和组件( component)有什么区别?

简单地说,在 React中元素(虛拟DOM)描述了你在屏幕上看到的DOM元素。

换个说法就是,在 React中元素是页面中DOM元素的对象表示方式。在 React中组件是一个函数或一个类,它可以接受输入并返回一个元素。

注意:工作中,为了提高开发效率,通常使用JSX语法表示 React元素(虚拟DOM)。在编译的时候,把它转化成一个 React. createElement调用方法。

参考:前端react面试题详细解答

在 ReactNative中,如何解决 adb devices找不到连接设备的问题?

在使用 Genymotion时,首先需要在SDK的 platform-tools中加入环境变量,然后在 Genymotion中单击 Setting,选择ADB选项卡,单击 Use custom Android SDK tools,浏览本地SDK的位置,单击OK按钮就可以了。启动虛拟机后,在cmd中输入 adb devices可以查看设备。

说说 React组件开发中关于作用域的常见问题。

在 EMAScript5语法规范中,关于作用域的常见问题如下。

(1)在map等方法的回调函数中,要绑定作用域this(通过bind方法)。

(2)父组件传递给子组件方法的作用域是父组件实例化对象,无法改变。

(3)组件事件回调函数方法的作用域是组件实例化对象(绑定父组件提供的方法就是父组件实例化对象),无法改变。

在 EMAScript6语法规范中,关于作用域的常见问题如下。

(1)当使用箭头函数作为map等方法的回调函数时,箭头函数的作用域是当前组件的实例化对象(即箭头函数的作用域是定义时的作用域),无须绑定作用域。

(2)事件回调函数要绑定组件作用域。

(3)父组件传递方法要绑定父组件作用域。

总之,在 EMAScript6语法规范中,组件方法的作用域是可以改变的。

生命周期调用方法的顺序是什么?

React生命周期分为三大周期,11个阶段,生命周期方法调用顺序分别如下。

(1)在创建期的五大阶段,调用方法的顺序如下。

  • getDetaultProps:定义默认属性数据。
  • getInitialState:初始化默认状态数据。
  • component WillMount:组件即将被构建。
  • render:渲染组件。
  • componentDidMount:组件构建完成

(2)在存在期的五大阶段,调用方法的顺序如下。

  • componentWillReceiveProps:组件即将接收新的属性数据。
  • shouldComponentUpdate:判断组件是否应该更新。
  • componnent WillUpdate:组件即将更新。
  • render:渲染组件。
  • componentDidUpdate:组件更新完成。

(3)在销毁期的一个阶段,调用方法 componentWillUnmount,表示组件即将被销毀。

React 中 refs 的作用是什么

  • RefsReact 提供给我们的安全访问 DOM元素或者某个组件实例的句柄
  • 可以为元素添加ref属性然后在回调函数中接受该元素在 DOM 树中的句柄,该值会作为回调函数的第一个参数返回

React- Router有几种形式?

有以下几种形式。

HashRouter,通过散列实现,路由要带#。

BrowerRouter,利用HTML5中 history API实现,需要服务器端支持,兼容性不是很好。

在 React 中如何处理事件

为了解决跨浏览器的兼容性问题,SyntheticEvent 实例将被传递给你的事件处理函数,SyntheticEvent是 React 跨浏览器的浏览器原生事件包装器,它还拥有和浏览器原生事件相同的接口,包括 stopPropagation()preventDefault()

比较有趣的是,React 实际上并不将事件附加到子节点本身。React 使用单个事件侦听器侦听顶层的所有事件。这对性能有好处,也意味着 React 在更新 DOM 时不需要跟踪事件监听器。

受控组件和非受控组件区别是啥?

  • 受控组件是 React 控制中的组件,并且是表单数据真实的唯一来源。
  • 非受控组件是由 DOM 处理表单数据的地方,而不是在 React 组件中。 尽管非受控组件通常更易于实现,因为只需使用refs即可从 DOM 中获取值,但通常建议优先选择受控制的组件,而不是非受控制的组件。 这样做的主要原因是受控组件支持即时字段验证,允许有条件地禁用/启用按钮,强制输入格式。

redux有什么缺点

  • 一个组件所需要的数据,必须由父组件传过来,而不能像flux中直接从store取。
  • 当一个组件相关数据更新时,即使父组件不需要用到这个组件,父组件还是会重新render,可能会有效率影响,或者需要写复杂的shouldComponentUpdate进行判断。

在使用 React Router时,如何获取当前页面的路由或浏览器中地址栏中的地址?

在当前组件的 props中,包含 location属性对象,包含当前页面路由地址信息,在 match中存储当前路由的参数等数据信息。可以直接通过 this .props使用它们。

在构造函数调用 super 并将 props 作为参数传入的作用是啥?

在调用 super() 方法之前,子类构造函数无法使用this引用,ES6 子类也是如此。将 props 参数传递给 super() 调用的主要原因是在子构造函数中能够通过this.props来获取传入的 props

传递 props

代码语言:javascript复制
class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    console.log(this.props); // { name: 'sudheer',age: 30 }
  }
}

没传递 props

代码语言:javascript复制
class MyComponent extends React.Component {
  constructor(props) {
    super();
    console.log(this.props); // undefined
    // 但是 Props 参数仍然可用
    console.log(props); // Prints { name: 'sudheer',age: 30 }
  }
  render() {
    // 构造函数外部不受影响
    console.log(this.props); // { name: 'sudheer',age: 30 }
  }
}

上面示例揭示了一点。props 的行为只有在构造函数中是不同的,在构造函数之外也是一样的。

useEffect(fn, []) 和 componentDidMount 有什么差异

useEffect 会捕获 props 和 state。所以即便在回调函数里,你拿到的还是初始的 props 和 state。如果想得到“最新”的值,可以使用 ref。

React有哪些优化性能的手段

类组件中的优化手段

  • 使用纯组件 PureComponent 作为基类。
  • 使用 React.memo 高阶函数包装组件。
  • 使用 shouldComponentUpdate 生命周期函数来自定义渲染逻辑。

方法组件中的优化手段

  • 使用 useMemo
  • 使用 useCallBack

其他方式

  • 在列表需要频繁变动时,使用唯一 id 作为 key,而不是数组下标。
  • 必要时通过改变 CSS 样式隐藏显示组件,而不是通过条件判断显示隐藏组件。
  • 使用 Suspense 和 lazy 进行懒加载,例如:
代码语言:javascript复制
import React, { lazy, Suspense } from "react";

export default class CallingLazyComponents extends React.Component {
  render() {
    var ComponentToLazyLoad = null;

    if (this.props.name == "Mayank") {
      ComponentToLazyLoad = lazy(() => import("./mayankComponent"));
    } else if (this.props.name == "Anshul") {
      ComponentToLazyLoad = lazy(() => import("./anshulComponent"));
    }

    return (
      <div>
        <h1>This is the Base User: {this.state.name}</h1>
        <Suspense fallback={<div>Loading...</div>}>
          <ComponentToLazyLoad />
        </Suspense>
      </div>
    )
  }
}

新版生命周期

在新版本中,React 官方对生命周期有了新的 变动建议:

  • 使用getDerivedStateFromProps替换componentWillMount;
  • 使用getSnapshotBeforeUpdate替换componentWillUpdate;
  • 避免使用componentWillReceiveProps

其实该变动的原因,正是由于上述提到的 Fiber。首先,从上面我们知道 React 可以分成 reconciliationcommit两个阶段,对应的生命周期如下:

reconciliation

  • componentWillMount
  • componentWillReceiveProps
  • shouldComponentUpdate
  • componentWillUpdate

commit

  • componentDidMount
  • componentDidUpdate
  • componentWillUnmount

Fiber 中,reconciliation 阶段进行了任务分割,涉及到 暂停 和 重启,因此可能会导致 reconciliation 中的生命周期函数在一次更新渲染循环中被 多次调用 的情况,产生一些意外错误

新版的建议生命周期如下:

代码语言:javascript复制
class Component extends React.Component {
  // 替换 `componentWillReceiveProps` ,
  // 初始化和 update 时被调用
  // 静态函数,无法使用 this
  static getDerivedStateFromProps(nextProps, prevState) {}

  // 判断是否需要更新组件
  // 可以用于组件性能优化
  shouldComponentUpdate(nextProps, nextState) {}

  // 组件被挂载后触发
  componentDidMount() {}

  // 替换 componentWillUpdate
  // 可以在更新之前获取最新 dom 数据
  getSnapshotBeforeUpdate() {}

  // 组件更新后调用
  componentDidUpdate() {}

  // 组件即将销毁
  componentWillUnmount() {}

  // 组件已销毁
  componentDidUnMount() {}
}

使用建议:

  • constructor初始化 state
  • componentDidMount中进行事件监听,并在componentWillUnmount中解绑事件;
  • componentDidMount中进行数据的请求,而不是在componentWillMount
  • 需要根据 props 更新 state 时,使用getDerivedStateFromProps(nextProps, prevState)
    • 旧 props 需要自己存储,以便比较;
代码语言:javascript复制
public static getDerivedStateFromProps(nextProps, prevState) {
    // 当新 props 中的 data 发生变化时,同步更新到 state 上
    if (nextProps.data !== prevState.data) {
        return {
            data: nextProps.data
        }
    } else {
        return null1
    }
}

可以在componentDidUpdate监听 props 或者 state 的变化,例如:

代码语言:javascript复制
componentDidUpdate(prevProps) {
    // 当 id 发生变化时,重新获取数据
    if (this.props.id !== prevProps.id) {
        this.fetchData(this.props.id);
    }
}
  • 在componentDidUpdate使用setState时,必须加条件,否则将进入死循环;
  • getSnapshotBeforeUpdate(prevProps, prevState)可以在更新之前获取最新的渲染数据,它的调用是在 render 之后, update 之前;
  • shouldComponentUpdate: 默认每次调用setState,一定会最终走到 diff 阶段,但可以通过shouldComponentUpdate的生命钩子返回false来直接阻止后面的逻辑执行,通常是用于做条件渲染,优化渲染的性能。

类组件和函数组件之间的区别是啥?

  • 类组件可以使用其他特性,如状态 state 和生命周期钩子。
  • 当组件只是接收 props 渲染到页面时,就是无状态组件,就属于函数组件,也被称为哑组件或展示组件。 函数组件和类组件当然是有区别的,而且函数组件的性能比类组件的性能要高,因为类组件使用的时候要实例化,而函数组件直接执行函数取返回结果即可。为了提高性能,尽量使用函数组件。

区别

函数组件

类组件

是否有 this

没有

是否有生命周期

没有

是否有状态 state

没有

根据下面定义的代码,可以找出存在的两个问题吗 ?

请看下面的代码:

在这里插入图片描述在这里插入图片描述

答案:

1.在构造函数没有将 props 传递给 super,它应该包括以下行

代码语言:javascript复制
constructor(props) {
super(props);
// ...
}

2.事件监听器(通过addEventListener()分配时)的作用域不正确,因为 ES6 不提供自动绑定。因此,开发人员可以在构造函数中重新分配clickHandler来包含正确的绑定:

代码语言:javascript复制
constructor(props) {
super(props);
this.clickHandler = this.clickHandler.bind(this);
// ...
}

redux中间件

中间件提供第三方插件的模式,自定义拦截 action -> reducer 的过程。变为 action -> middlewares -> reducer。这种机制可以让我们改变数据流,实现如异步actionaction 过滤,日志输出,异常报告等功能

  • redux-logger:提供日志输出
  • redux-thunk:处理异步操作
  • redux-promise:处理异步操作,actionCreator的返回值是promise

1 人点赞