写点什么

百度前端 react 面试题总结

作者:beifeng1996
  • 2022-11-04
    浙江
  • 本文字数:5012 字

    阅读完需:约 16 分钟

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


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


没传递 props


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 进行懒加载,例如:


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 中的生命周期函数在一次更新渲染循环中被 多次调用 的情况,产生一些意外错误


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


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 需要自己存储,以便比较;


public static getDerivedStateFromProps(nextProps, prevState) {    // 当新 props 中的 data 发生变化时,同步更新到 state 上    if (nextProps.data !== prevState.data) {        return {            data: nextProps.data        }    } else {        return null1    }}
复制代码


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


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 渲染到页面时,就是无状态组件,就属于函数组件,也被称为哑组件或展示组件。函数组件和类组件当然是有区别的,而且函数组件的性能比类组件的性能要高,因为类组件使用的时候要实例化,而函数组件直接执行函数取返回结果即可。为了提高性能,尽量使用函数组件。


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

请看下面的代码:



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


constructor(props) {super(props);// ...}
复制代码


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


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

用户头像

beifeng1996

关注

还未添加个人签名 2022-09-01 加入

还未添加个人简介

评论

发布
暂无评论
百度前端react面试题总结_React_beifeng1996_InfoQ写作社区