React面试总结

467 阅读3分钟

jsx

jsx本质

JSX 其实就是 JavaScript 对象。

是React.createElement的语法糖

所有jsx最终都会被转换成React.createElement的函数调用。 若直接使用React.creatElement()则不需要babel转码。

image.png

react16 必须引用react

image.png 打印结果 image.png 因为引用了React.createElement所以必须引用react

react17不用

image.png

image.png

reactNode和reactElement区别

reactElement表示一个元素

image.png

reactNode image.png

虚拟dom

虚拟dom优点

image.png

缺点

image.png

源码

image.png

createELement()实现我就不写了。。。

函数组件与类组件

相同点和不同点

类组件原型上挂在了isreactComponent属性

image.png

为什么类组件创建需要保存实例 image.png 函数组件具有捕获特性

函数捕获性连接

函数捕获性解释2 image.png

image.png

component和purecomponent

相当于多了一个shouldComponent做了一个浅比较判断

image.png

React.memo

效果跟purecomponent一样 image.png

类组件为啥要保存实例

因为不保存的话,更新不了了

react渲染流程

设计理念

image.png

异步可终中断是指fiber把一个大的js任务分割成小的js任务,放在浏览器空余时间来处理,这样就不会卡顿

增量更新是指比如原先数组是10个 这时候给数组添加一个元素,就会在原来的基础上在增加一个新元素,之前的10个不会发生改变

js线程和渲染线程互斥是因为,因为js可以操作 dom,比如js要删除一个元素,浏览器要画 image.png

image.png

image.png

异步渲染的FAQ

这种异步写法有啥缺点?

把执行时间边长了,因为执行一会休息一会,原来是一股气直接执行完毕 image.png

image.png

react不是使用requestIdleCallback

这种调度方式怎么兼容以前的生命周期

如果这3个生命周期还存在的话,切片就会多次调用到 image.png 因为fiber在diff阶段可以被中断,可以多次渲染上面三个周期

好文分享 时间切片实现

image.png

        function sleep(dely){
            for(var ts=Date.now();Date.now()-ts<=dely;){}
        }
        const tasks=[
            function(){
                console.log('1 start')
                sleep(10)
                console.log('1 end')
            },
            function(){
                console.log('2 start')
                sleep(10)
                console.log('2 end')
            },
            function(){
                console.log('3 start')
                sleep(10)
                console.log('3 end')
            },
        ]

        requestIdleCallback(myNonEssentialWork);
    
    
    function myNonEssentialWork (deadline) {
    console.log(deadline.timeRemaining())
      // deadline.timeRemaining()可以获取到当前帧剩余时间
      while (deadline.timeRemaining() > 0 && tasks.length > 0) {
        doWorkIfNeeded();
      }
      if (tasks.length > 0){
        requestIdleCallback(myNonEssentialWork);
      }
    //
    }

    function doWorkIfNeeded(){
        let m=tasks.shift()
        m()
    }

fiber

image.png

image.png image.png

image.png

执行阶段

image.png

渲染

image.png

fiber树跟虚拟dom比多了tag和链条

image.png

副作用列表

image.png fiber 部分源码

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <div id="root"></div>
    <script>
        let style = { color: "green", border: '1px solid red' }
        let A = {
            type: 'div',
            key: 'A',
            props: {
                style,
                children: [
                    // "A",
                    { type: 'div', key: "B1", props: { style, children: [] } },
                    { type: 'div', key: "B2", props: { style, children: [] } },
                ]
            }
        }
        //表示一个工作单元,表示正在处理中的fiber
        let workInprogress;
        const TAG_ROOT = "TAG_ROOT"//fiber的根节点
        const TAG_HOST = "TAG_HOST" //fiber原生节点
        const placement = "placement"

        let root = document.getElementById('root')

        //fiber是一个普通的js对象
        let rootfiber = {
            tag: TAG_ROOT, //fiber类型
            key: 'ROOT',// 唯一标签
            stateNode: root, //fiber对应的真是dom节点
            props: {
                children: [A]
            }
        }
        function workLoop() {
            while (workInprogress) {//如果有任务就执行
                console.log(2)
                workInprogress = performUnitOfwork(workInprogress)
            }
            console.log(rootfiber, 99999999);
            commitRoot(rootfiber)
        }

        function commitRoot(rootfiber) {
            let currentEffect = rootfiber.firstEffect;
            while (currentEffect) {
                let flags = currentEffect.flags;
                switch (flags) {
                    case placement:
                        commitPlacement(currentEffect)
                }
                currentEffect = currentEffect.nextEffect
            }
        }

        function commitPlacement(currentEffect) {
            let parent = currentEffect.return.stateNode;// 父dom节点
            console.log(parent,8)
            parent.appendChild(currentEffect.stateNode)
        }
        function performUnitOfwork(workInprogress) {

            //构建子fiber树
            // 父fiber。child是大儿子 silbing是二儿子
            beginWork(workInprogress)
            if (workInprogress.child) {// 如果创建完子fiber链表后有大儿子
                return workInprogress.child  //则返回处理太子,构建太子的儿子们
            }
            //r如果没有儿子,开始构建弟弟
            while (workInprogress) {
                completeUnitofwork(workInprogress)  //如果没有儿子 自己就结束了了
                if (workInprogress.sibling) {
                    console.log(333)
                    return workInprogress.sibling
                }
                //如果没有弟弟 就找叔叔
                workInprogress = workInprogress.return
                //如果没有父亲 循环就结束了
            }

        }
        // fiber在结束的时候 要创建真是的dom元素
        function completeUnitofwork(workInprogress) {
            let stateNode //真是dom
            switch (workInprogress.tag) {
                case TAG_HOST:
                    stateNode = createStateNode(workInprogress)
                    break;
            }
            // 在完成工作单元的时候要判断当前的fiber节点有没有dom操作
            // effectList副作用链并不是包含所有节点,而是包括有副作用的fiber节点,
            makeEffectList(workInprogress)
        }


        function makeEffectList(completeWork) {
            let returnFiber = completeWork.return
            if (returnFiber) {
                if (!returnFiber.firstEffect) {
                    returnFiber.firstEffect = completeWork.firstEffect
                }
                if (completeWork.lastEffect) {
                    if (returnFiber.lastEffect) {
                        returnFiber.lastEffect.nextEffect = completeWork.firstEffect
                    }
                    returnFiber.lastEffect = completeWork.lastEffect
                }
                if (completeWork.flags) {
                    if (returnFiber.lastEffect) {
                        returnFiber.lastEffect.nextEffect = completeWork
                    } else {
                        returnFiber.firstEffect = completeWork
                    }
                    returnFiber.lastEffect = completeWork
                }
            }
        }
        function createStateNode(fiber) {
            if (fiber.tag === TAG_HOST) {
                let stateNode = document.createElement(fiber.type)
                fiber.stateNode = stateNode
            }
            return fiber.stateNode
        }
        /*
          根据当前的fiber和虚拟dom构建fiber树
        */
        function beginWork(workInprogress) {
            let nextChildren = workInprogress.props.children
            //会根据父FIBER和所有的儿子(虚拟dom)构建出子fiber树。只有一层
            //先让父亲吧儿子一个一个生出来,然后再看孙子
            return reconcilehildren(workInprogress, nextChildren)
        }
        // 根据父Fiber和子虚拟DOM数组,构建当前returnFiber的子fiber
        function reconcilehildren(retrunFiber, nextChildren) {
            let previousNewFiber;// 上一个Fiber儿子
            let firstChildFiber;// 当前returnzfiber的大儿子
            for (let newIndex = 0; newIndex < nextChildren.length; newIndex++) {
                let newFiber = createfiber(nextChildren[newIndex])
                newFiber.flags = placement //这是一个新节点,肯定要插入到dom中
                newFiber.return = retrunFiber
                if (!firstChildFiber) { //如果没有大儿子,就要先给大儿子赋值
                    firstChildFiber = newFiber
                } else {
                    previousNewFiber.sibling = newFiber
                }
                previousNewFiber = newFiber
            }
            retrunFiber.child = firstChildFiber
            return firstChildFiber //返回大儿子

        }

        function createfiber(element) {
            return {
                tag: TAG_HOST,
                type: element.type,
                key: element.key,
                props: element.props
            }

        }
        //当前正在执行的工作单元
        workInprogress = rootfiber
        workLoop()

        //开始
    </script>

</body>

</html>

diff算法

image.png image.png

规则

单节点操作

image.png

多节点操作

image.png

key相同,type也相同,属性不同 image.png

key相同但是type不同

image.png

复杂情况

image.png

image.png image.png

删除操作先执行 image.png

key的作用

image.png

合成事件

image.png image.png

react16 是把所有的注册docuemnt上

image.png image.png

react17注册在容器上

image.png

image.png image.png

setState

image.png

同步或者异步

react18之后 都是异步批量了,只要使用createroot

image.png

0 0 2 3

如果通过reactDom.unstable_batchUpdates就是0 0 1 1

image.png 如果setState用函数来写就是 0 0 2 2

如果setState传的是函数的话 会接着上一次返回值 一个一个处理

image.png

image.png

image.png

并发非并发

image.png

数据合并

state数据合并

state之前有name 现在更新age,不用担心name会消失,因为react内部会做一遍处理 image.png

本身数据合并

image.png

如果参数是函数的话 会进行累加 image.png

react样式

内联样式

image.png

cssmodule

image.png

image.png

生成后的样子 image.png

css in js

image.png

styleComponent

image.png

FAQ

react中key作用

提高diff的同级比较的效率,避免原地复用带来的副作用

ref是什么

ref提供了一种方式,允许我们访问dom节点或在render方法中创建的react的元素

react性能优化

1 shouldcomponentuodate purecomponnet,react.memo来减少不必要的渲染

2 数据缓存

usememo缓存参数,usecallback缓存函数

image.png

image.png

3,useReducer():使用useReducer可以减少useState的使用,减少VDOM的render次数,也便于统一管理多变量

生命周期

image.png

getderivedstateFormProps

image.png getderivedstateFormProps

shouldComponentUpdate

 shouldComponentUpdate(newProps,newState){
    console.log(newProps.no,newState.no)
    return (newProps.no%2==0 && newState.no%2==0) ? true: false;
  }

useeffect和componentDidmount有什么区别

image.png

useeffect会在fiber上插入passive

image.png image.png classcomponent image.png

render阶段与commit阶段传了一条effect链表

image.png

commit阶段 CdM时机

image.png

对于passive来说会在上图三个阶段结束后异步调用useeffect的回调函数

image.png

uselayoutEffect和CdM调用时机一致