前端面试题大合集4----框架篇(React)

05-14 1289阅读

一、React 合成事件

Dom事件流分三个阶段:事件捕获阶段,目标阶段,事件冒泡阶段

前端面试题大合集4----框架篇(React)
(图片来源网络,侵删)

React在事件绑定时有一套自身的机制,就是合成事件。如下比较直观:

react中事件绑定:
普通的事件绑定:

React合成事件机制:React并不是将click事件直接绑定在dom上面,而是采用事件冒泡的形式冒泡到document上面,然后React将事件封装给正式的函数运行和处理。

React合成事件原理:

  1. 当用户在为onClick添加函数时,React并没有将Click绑定到Dom上;
  2. 而是document处监听所有支持的事件,当事件发生并冒泡至document处时,React将事件内容封装交给中间层SyntheticEvent (负责所有事件合成);
  3. 然后使用统一的分发函数dispatchEvent,将封装的事件内容交由真正的处理函数执行。

React中也可以使用原生事件,合成事件和原生事件也可以混合使用:

class Demo extends React.Component {
    componentDidMount() {
        const $this = ReactDOM.findDOMNode(this)
        $this.addEventListener('click', this.onDOMClick, false)
    }
    onDOMClick = evt => {
        console.log('dom event')
    }
    
    onClick = evt => {
        console.log('react event')
    }
    render() {
        return (
            Demo
        )
    }
}

React中阻止事件冒泡调用:evt.stopPropagation()方法,由于Dom事件被阻止了,无法到达document,所以合成事件自然不会被触发。


二、React中的setState,什么时候时同步的,什么时候时异步的?

1. react18版本之前

  • setState在不同情况下可以表现为异步或同步
    1. 在Promise的状态更新、js原生事件、setTimeout、setInterval中是同步的。
    2. 在react的合成事件中,是异步的

    2. react18版本之后。

    • setState都会表现为异步(即批处理)。
      1. 批处理:是指 React将多个状态更新分组到单个重新渲染中以获得更好的性能
      2. 如果同一点击事件中有两个状态更新,React 总是将它们批处理为一次重新渲染。如果运行以下代码,您将看到每次单击时,尽管您设置了两次状态,React 只执行一次渲染
      function App() {
        const [count, setCount] = useState(0);
        const [flag, setFlag] = useState(false);
        function handleClick() {
          setCount(c => c + 1); // Does not re-render yet
          setFlag(f => !f); // Does not re-render yet
          // React will only re-render once at the end (that's batching!)
        }
        return (
          
            Next
            { color: flag ? "blue" : "black" }}{count}
          
        );
      }
      • 总结

        setState 只在合成事件和 hook() 中是“异步”的,在 原生事件和 setTimeout 中都是同步的。

        三、React Hook相关知识
        1、请勿在循环或者条件语句中使用hook

        因为React Hook是以单向循环链表的形式存储的,即是有序的。

        循环是为了从最后一个节点移动到下一个节点的时候,只需要通过next一步就可以拿到第一个节点。

        React Hook的执行,分为mount和update阶段。在mount阶段,通过mountWorkInProgressHook()创建各个hooks(如useState、 useMemo、useEffect、useCallBack等)并且将当前hook添加到表尾。在update阶段,在获取或者更新hooks只的时候,会先获取当前hook的状态:hook.memoizedState,并且按照顺序读取或更新hook,若在条件判断或者循环中使用hooks,那么在更新阶段若增加或者减少了某个hook,hooks的数量发生变化,而React是按照顺序,通过next读取下一个hook,则导致后面的hooks和挂载阶段对应不上,发生读写错误的情况,从而引发bug。

        React为什么要以单向循环链表的形式存储hooks呢?直接以key-value的对象形式存储就可以在循环或条件语句中使用hooks了,岂不更好?

        这是因为react scheduler的调度策略如此,以链表的形式存储是为了可以实现并发、可打断、可恢复、可继续执行下一个fiber任务。

        2、使用map循环数组渲染列表时须指定唯一且稳定的key

        react中的key本质时服务于diff算法的,它的默认值是null,在diff算法中,新旧节点是否可以复用,首先就会判断key是否相同,其后才会进行其他条件的判定(如props),从而提升渲染性能,减少重复无效的渲染。

        为什么在渲染列表的时候,不能讲index设置为key?

        因为显式的把index设置为key,和不设置效果是一样的,这就是所谓的就地复用原则,即react在diff的时候,如果没有key,就会在老虚拟Dom树中,找到对应顺序位置的组件,然后对比组件的类型和props来决定是否需要重新渲染。

        index作为key,不仅会在数组发生变化的时候,造成无效多余的渲染,还可能在组件含有非受控组件的时候,造成UI渲染错误。

        如果渲染列表的时候,key重复了会怎么样?

        首先react会给你输出警告,告诉你key值应该是唯一的,以便组件在更新期间保持其标识。重复的key可能导致子节点被重复使用或省略,从而引发UI bug。

        3、memo

        在react中,当我们setState之后,若值发生变化,则会重新render当前组件以及其子组件,在必要的时候,我们可以使用memo进行优化,来减少无效渲染。memo是一个高阶组件,接受一个组件为参数,并返回一个原组件为基础的新组件,而在memo内部,则会使用Object.is来遍历对比新旧props是否发生变化,以决定是否需要重新render。

        4、useMemo和useCallback

        它两个都是用来缓存数据,优化性能的。

        • 共同作用

          在依赖数据发生变化的时候,才会调用传进去的回调函数去重新计算结果,起到一个缓存的作用

          • 两者的区别

            useMemo  缓存的结果是回调函数中return回来的值,主要用于缓存计算结果的值,应用场景如需要计算的状态。

            useCallback  缓存的结果是函数,主要用于缓存函数,应用场景如需要缓存的函数,因为函数式组件每次任何一个state发生变化,会触发整个组件更新,一些函数是没有必要更新的,此时就应该缓存起来,提高性能,减少对资源的浪费;另外还需要注意的是,useCallback应该和React.memo配套使用,缺了一个都可能导致性能不升反而下降。

            5、useImperativeHandle (forwardRef)

            某个组件想要暴漏一些方法,来供外部组件来调用。就需要用这个hook,需要和forwardRef来配合使用。

            6、获取最新的state

            在react中,setState之后,是采用异步调度、批量更新的策略,导致我们无法直接获取最新的state。

            在使用class组件的时候,我们可以通过传递第二个参数,传一个回调函数来获取最新的state,但是在React18版本之后,就算在class component里面,setTimeout,原生事件回调里,也是异步批量更新了。

            在hooks里面,我们目前只能通过useEffect,把当前state当作依赖传入,来在useEffect回调函数里面获取最新的state。

            7、useRef

            如果我们想在hooks里面获取同步最新的值,可以使用useRef;创建一个ref对象,然后挂载到hook.memoizedState,我们在修改的时候,就是直接修改ref.current。useRef其实就是提供一个稳定的变量,在组件的整个生命周期都是持续存在且是同一个引用。

            注意:修改useRef返回的状态不会引起UI的重渲染。

VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]