• 一天梳理完react面试高频知识点


    描述事件在 React中的处理方式。

    为了解决跨浏览器兼容性问题, React中的事件处理程序将传递 SyntheticEvent的实例,它是跨浏览器事件的包装器。这些 SyntheticEvent与你习惯的原生事件具有相同的接口,它们在所有浏览器中都兼容。
    React实际上并没有将事件附加到子节点本身。而是通过事件委托模式,使用单个事件监听器监听顶层的所有事件。这对于性能是有好处的。这也意味着在更新DOM时, React不需要担心跟踪事件监听器。

    React 中的key是什么?为什么它们很重要?

    key可以帮助 React跟踪循环创建列表中的虚拟DOM元素,了解哪些元素已更改、添加或删除。
    每个绑定key的虚拟DOM元素,在兄弟元素之间都是独一无二的。在 React的和解过程中,比较新的虛拟DOM树与上一个虛拟DOM树之间的差异,并映射到页面中。key使 React处理列表中虛拟DOM时更加高效,因为 React可以使用虛拟DOM上的key属性,快速了解元素是新的、需要删除的,还是修改过的。如果没有key,Rεat就不知道列表中虚拟DOM元素与页面中的哪个元素相对应。所以在创建列表的时候,不要忽略key。

    react旧版生命周期函数

    初始化阶段

    • getDefaultProps:获取实例的默认属性
    • getInitialState:获取每个实例的初始化状态
    • componentWillMount:组件即将被装载、渲染到页面上
    • render:组件在这里生成虚拟的DOM节点
    • componentDidMount:组件真正在被装载之后

    运行中状态

    • componentWillReceiveProps:组件将要接收到属性的时候调用
    • shouldComponentUpdate:组件接受到新属性或者新状态的时候(可以返回false,接收数据后不更新,阻止render调用,后面的函数不会被继续执行了)
    • componentWillUpdate:组件即将更新不能修改属性和状态
    • render:组件重新描绘
    • componentDidUpdate:组件已经更新

    销毁阶段

    • componentWillUnmount:组件即将销毁

    React中Diff算法的原理是什么?

    原理如下。
    (1)节点之间的比较。
    节点包括两种类型:一种是 React组件,另一种是HTML的DOM。
    如果节点类型不同,按以下方式比较。
    如果 HTML DOM不同,直接使用新的替换旧的。如果组件类型不同,也直接使用新的替换旧的。
    如果 HTML DOM类型相同,按以下方式比较。
    在 React里样式并不是一个纯粹的字符串,而是一个对象,这样在样式发生改变时,只需要改变替换变化以后的样式。修改完当前节点之后,递归处理该节点的子节点。
    如果组件类型相同,按以下方式比较。
    如果组件类型相同,使用 React机制处理。一般使用新的 props替换旧的 props,并在之后调用组件的 componentWillReceiveProps方法,之前组件的 render方法会被调用。
    节点的比较机制开始递归作用于它的子节点。
    (2)两个列表之间的比较。
    一个节点列表中的一个节点发生改变, React无法很妤地处理这个问题。循环新旧两个列表,并找出不同,这是 React唯一的处理方法。
    但是,有一个办法可以把这个算法的复杂度降低。那就是在生成一个节点列表时给每个节点上添加一个key。这个key只需要在这一个节点列表中唯一,不需要全局唯一。
    (3)取舍
    需要注意的是,上面的启发式算法基于两点假设。
    类型相近的节点总是生成同样的树,而类型不同的节点也总是生成不同的树
    可以为多次 render都表现稳定的节点设置key。
    上面的节点之间的比较算法基本上就是基于这两个假设而实现的。要提高 React应用的效率,需要按照这两点假设来开发。

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

    请说岀 React从 EMAScript5编程规范到 EMAScript6编程规范过程中的几点改变。

    主要改变如下。
    (1)创建组件的方法不同。
    EMAScript5版本中,定义组件用 React.createClass。EMAScript6版本中,定义组件要定义组件类,并继承 Component类。
    (2)定义默认属性的方法不同。
    EMAScript5版本中,用 getDefaultProps定义默认属性。EMAScript6版本中,为组件定义 defaultProps静态属性,来定义默认属性。
    (3)定义初始化状态的方法不同。EMAScript5版本中,用 getInitialState定义初始化状态。EMAScript6版本中,在构造函数中,通过this. state定义初始化状态。
    注意:构造函数的第一个参数是属性数据,一定要用 super继承。
    (4)定义属性约束的方法不同。
    EMAScript5版本中,用 propTypes定义属性的约束。
    EMAScript6版本中,为组件定义 propsTypes静态属性,来对属性进行约束。
    (5)使用混合对象、混合类的方法不同。
    EMAScript5版本中,通过mixins继承混合对象的方法。
    EMAScript6版本中,定义混合类,让混合类继承 Component类,然后让组件类继承混合类,实现对混合类方法的继承。
    (6)绑定事件的方法不同。
    EMAScript5版本中,绑定的事件回调函数作用域是组件实例化对象。
    EMAScript6版本中,绑定的事件回调函数作用域是null。
    (7)父组件传递方法的作用域不同。
    EMAScript5版本中,作用域是父组件。 EMAScript6版本中,变成了null。
    (8)组件方法作用域的修改方法不同。
    EMAScript5版本中,无法改变作用域。
    EMAScript6版本中,作用域是可以改变的。

    React Portal 有哪些使用场景

    • 在以前, react 中所有的组件都会位于 #app 下,而使用 Portals 提供了一种脱离 #app 的组件
    • 因此 Portals 适合脱离文档流(out of flow) 的组件,特别是 position: absolute 与 position: fixed的组件。比如模态框,通知,警告,goTop 等。

    以下是官方一个模态框的示例,可以在以下地址中测试效果

    <html>
      <body>
        <div id="app">div>
        <div id="modal">div>
        <div id="gotop">div>
        <div id="alert">div>
      body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    const modalRoot = document.getElementById('modal');
    
    class Modal extends React.Component {
      constructor(props) {
        super(props);
        this.el = document.createElement('div');
      }
    
      componentDidMount() {
        modalRoot.appendChild(this.el);
      }
    
      componentWillUnmount() {
        modalRoot.removeChild(this.el);
      }
    
      render() {
        return ReactDOM.createPortal(
          this.props.children,
          this.el,
        );
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    React Hooks当中的useEffect是如何区分生命周期钩子的

    useEffect可以看成是componentDidMountcomponentDidUpdatecomponentWillUnmount三者的结合。useEffect(callback, [source])接收两个参数,调用方式如下

    useEffect(() => {
       console.log('mounted');
    
       return () => {
           console.log('willUnmount');
       }
     }, [source]);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    生命周期函数的调用主要是通过第二个参数[source]来进行控制,有如下几种情况:

    • [source]参数不传时,则每次都会优先调用上次保存的函数中返回的那个函数,然后再调用外部那个函数;
    • [source]参数传[]时,则外部的函数只会在初始化时调用一次,返回的那个函数也只会最终在组件卸载时调用一次;
    • [source]参数有值时,则只会监听到数组中的值发生变化后才优先调用返回的那个函数,再调用外部的函数。

    react-router里的标签和标签有什么区别

    对比,Link组件避免了不必要的重渲染

    react 的渲染过程中,兄弟节点之间是怎么处理的?也就是key值不一样的时候

    通常我们输出节点的时候都是map一个数组然后返回一个ReactNode,为了方便react内部进行优化,我们必须给每一个reactNode添加key,这个key prop在设计值处不是给开发者用的,而是给react用的,大概的作用就是给每一个reactNode添加一个身份标识,方便react进行识别,在重渲染过程中,如果key一样,若组件属性有所变化,则react只更新组件对应的属性;没有变化则不更新,如果key不一样,则react先销毁该组件,然后重新创建该组件

    什么是 React的refs?为什么它们很重要

    refs允许你直接访问DOM元素或组件实例。为了使用它们,可以向组件添加个ref属性。
    如果该属性的值是一个回调函数,它将接受底层的DOM元素或组件的已挂载实例作为其第一个参数。可以在组件中存储它。

    export class App extends Component {
      showResult() {
        console.log(this.input.value);
      }
      render() {
        return (
          <div>
            <input type="text" ref={(input) => (this.input = input)} />
            <button onClick={this.showResult.bind(this)}>展示结果</button>
          </div>
        );
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    如果该属性值是一个字符串, React将会在组件实例化对象的refs属性中,存储一个同名属性,该属性是对这个DOM元素的引用。可以通过原生的 DOM API操作它。

    export class App extends Component {
      showResult() {
        console.log(this.refs.username.value);
      }
      render() {
        return (
          <div>
            <input type="text" ref="username" />
            <button onClick={this.showResu1t.bind(this)}>展示结果</button>
          </div>
        );
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

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

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

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

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

    我现在有一个button,要用react在上面绑定点击事件,要怎么做?

    class Demo {
      render() {
        return 
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    你觉得你这样设置点击事件会有什么问题吗?

    由于onClick使用的是匿名函数,所有每次重渲染的时候,会把该onClick当做一个新的prop来处理,会将内部缓存的onClick事件进行重新赋值,所以相对直接使用函数来说,可能有一点的性能下降

    修改

    class Demo {
    
      onClick = (e) => {
        alert('我点击了按钮')
      }
    
      render() {
        return <button onClick={this.onClick}>
          按钮
        </button>
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    这段代码有什么问题?

    class App extends Component {
      constructor(props) {
        super(props);
        this.state = {
          username: "有课前端网",
          msg: " ",
        };
      }
      render() {
        return <div> {this.state.msg}</div>;
      }
      componentDidMount() {
        this.setState((oldState, props) => {
          return {
            msg: oldState.username + " - " + props.intro,
          };
        });
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    render ( < App intro=" 前端技术专业学习平台">,ickt )
    在页面中正常输出“有课前端网-前端技术专业学习平台”。但是这种写法很少使用,并不是常用的写法。React允许对 setState方法传递一个函数,它接收到先前的状态和属性数据并返回一个需要修改的状态对象,正如我们在上面所做的那样。它不但没有问题,而且如果根据以前的状态( state)以及属性来修改当前状态,推荐使用这种写法。

    什么是纯函数?

    纯函数是不依赖并且不会在其作用域之外修改变量状态的函数。本质上,纯函数始终在给定相同参数的情况下返回相同结果。

    key的作用

    是给每一个 vnode 的唯一 id,可以依靠 key,更准确,更快的拿到 oldVnode 中对应的 vnode 节点

    
    <div>
      <p key="ka">kap>
      <h3 key="song">songhe>
    div>
    
    
    <div>
      <h3 key="song">songh3>
      <p key="ka">kap>
    div>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    如果没有 key,React 会认为 div 的第一个子节点由 p 变成 h3,第二个子节点由 h3 变成 p,则会销毁这两个节点并重新构造。

    但是当我们用 key 指明了节点前后对应关系后,React 知道 key === "ka" 的 p 更新后还在,所以可以复用该节点,只需要交换顺序。

    key 是 React 用来追踪哪些列表元素被修改、被添加或者被移除的辅助标志。

    在开发过程中,我们需要保证某个元素的 key 在其同级元素中具有唯一性。在 React diff 算法中,React 会借助元素的 Key 值来判断该元素是新近创建的还是被移动而来的元素,从而减少不必要的元素重新渲染。同时,React 还需要借助 key 来判断元素与本地状态的关联关系。

    setState方法的第二个参数有什么用?使用它的目的是什么?

    它是一个回调函数,当 setState方法执行结束并重新渲染该组件时调用它。在工作中,更好的方式是使用 React组件生命周期之——“存在期”的生命周期方法,而不是依赖这个回调函数。

    export class App extends Component {
      constructor(props) {
        super(props);
        this.state = {
          username: "雨夜清荷",
        };
      }
      render() {
        return <div> {this.state.username}</div>;
      }
      componentDidMount() {
        this.setstate(
          {
            username: "有课前端网",
          },
          () => console.log("re-rendered success. ")
        );
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    React 16中新生命周期有哪些

    关于 React16 开始应用的新生命周期: 可以看出,React16 自上而下地对生命周期做了另一种维度的解读:

    • Render 阶段:用于计算一些必要的状态信息。这个阶段可能会被 React 暂停,这一点和 React16 引入的 Fiber 架构(我们后面会重点讲解)是有关的;
    • Pre-commit阶段:所谓“commit”,这里指的是“更新真正的 DOM 节点”这个动作。所谓 Pre-commit,就是说我在这个阶段其实还并没有去更新真实的 DOM,不过 DOM 信息已经是可以读取的了;
    • Commit 阶段:在这一步,React 会完成真实 DOM 的更新工作。Commit 阶段,我们可以拿到真实 DOM(包括 refs)。

    与此同时,新的生命周期在流程方面,仍然遵循“挂载”、“更新”、“卸载”这三个广义的划分方式。它们分别对应到:

    • 挂载过程:
      • constructor
      • getDerivedStateFromProps
      • render
      • componentDidMount
    • 更新过程:
      • getDerivedStateFromProps
      • shouldComponentUpdate
      • render
      • getSnapshotBeforeUpdate
      • componentDidUpdate
    • 卸载过程:
      • componentWillUnmount

    在React中页面重新加载时怎样保留数据?

    这个问题就设计到了数据持久化, 主要的实现方式有以下几种:

    • Redux: 将页面的数据存储在redux中,在重新加载页面时,获取Redux中的数据;
    • data.js: 使用webpack构建的项目,可以建一个文件,data.js,将数据保存data.js中,跳转页面后获取;
    • sessionStorge: 在进入选择地址页面之前,componentWillUnMount的时候,将数据存储到sessionStorage中,每次进入页面判断sessionStorage中有没有存储的那个值,有,则读取渲染数据;没有,则说明数据是初始化的状态。返回或进入除了选择地址以外的页面,清掉存储的sessionStorage,保证下次进入是初始化的数据
    • history API: History API 的 pushState 函数可以给历史记录关联一个任意的可序列化 state,所以可以在路由 push 的时候将当前页面的一些信息存到 state 中,下次返回到这个页面的时候就能从 state 里面取出离开前的数据重新渲染。react-router 直接可以支持。这个方法适合一些需要临时存储的场景。

    diff 算法?

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

    新版生命周期

    在新版本中,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() {}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    使用建议:

    • 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
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

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

    componentDidUpdate(prevProps) {
        // 当 id 发生变化时,重新获取数据
        if (this.props.id !== prevProps.id) {
            this.fetchData(this.props.id);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 在componentDidUpdate使用setState时,必须加条件,否则将进入死循环;
    • getSnapshotBeforeUpdate(prevProps, prevState)可以在更新之前获取最新的渲染数据,它的调用是在 render 之后, update 之前;
    • shouldComponentUpdate: 默认每次调用setState,一定会最终走到 diff 阶段,但可以通过shouldComponentUpdate的生命钩子返回false来直接阻止后面的逻辑执行,通常是用于做条件渲染,优化渲染的性能。
  • 相关阅读:
    报错分析nest--嵌套
    TIS-prescan
    东莞理工学院举办第二届“火焰杯”软件测试高校就业选拔赛颁奖典礼
    基于Kintex UltraScale系列FPGA KU060/KU115高性能PCIe数据预处理载板(5GByte/s带宽)
    App测试时常用的adb命令你都掌握了哪些呢?
    申请发明专利的步骤有哪些?
    目标检测算法——YOLOv5结合ConvNeXt结构
    云计算实验(HCL模拟器)
    242 h155 最小栈
    计算机视觉(CV)技术的优势和挑战
  • 原文地址:https://blog.csdn.net/beifeng11996/article/details/127781611