• React组件什么时候render


    开篇先来个提问:对于如下Demo,点击Parent组件的div,触发更新,Son组件会打印child render!么?

    function Son() {
      console.log('child render!');
      return <div>Son</div>;
    }
    
    
    function Parent(props) {
      const [count, setCount] = React.useState(0);
    
      return (
        <div onClick={() => {setCount(count + 1)}}>
          count:{count}
          {props.children}
        </div>
      );
    }
    
    
    function App() {
      return (
        <Parent>
          <Son/>
        </Parent>
      );
    }
    
    const rootEl = document.querySelector("#root");
    ReactDOM.render(<App/>, rootEl);
    答案是“不会”
                                                                                                                                                   不会    

    render需要满足的条件

    React创建Fiber树时,每个组件对应的fiber都是通过如下两个逻辑之一创建的:

    • render。即调用render函数,根据返回的JSX创建新的fiber

    • bailout。即满足一定条件时,React判断该组件在更新前后没有发生变化,则复用该组件在上一次更新的fiber作为本次更新的fiber

    可以看到,当命中bailout逻辑时,是不会调用render函数的。

    所以,Son组件不会打印child render!是因为命中了bailout逻辑。

    bailout需要满足的条件

    什么情况下会进入bailout逻辑?当同时满足如下4个条件时:

    1. oldProps === newProps ?

    即本次更新的props(newProps)不等于上次更新的props(oldProps)。

    注意这里是全等比较

    我们知道组件render会返回JSXJSXReact.createElement的语法糖。

    所以render的返回结果实际上是React.createElement的执行结果,即一个包含props属性的对象。

    即使本次更新与上次更新props中每一项参数都没有变化,但是本次更新是React.createElement的执行结果,是一个全新的props引用,所以oldProps !== newProps

    如果我们使用了PureComponentMemo,那么在判断是进入render还是bailout时,不会判断oldPropsnewProps是否全等,而是会对props内每个属性进行浅比较。

    2. context没有变化

    contextvalue没有变化。

    3. workInProgress.type === current.type ?

    更新前后fiber.type是否变化,比如div是否变为p

    4. !includesSomeLane(renderLanes, updateLanes) ?

    当前fiber上是否存在更新,如果存在那么更新优先级是否和本次整棵fiber树调度的优先级一致?

    如果一致则进入render逻辑。

    就我们的Demo来说,Parent是整棵树中唯一能触发更新的组件(通过调用setCount)。

    所以Parent对应的fiber是唯一满足条件4的fiber

    Demo的详细执行逻辑

    所以,Demo中Son进入bailout逻辑,一定是同时满足以上4个条件。我们一个个来看。

    条件2,Demo中没有用到context,满足。

    条件3,更新前后type都为Son对应的函数组件,满足。

    条件4,Son本身无法触发更新,满足。

    所以,重点是条件1。让我们详细来看下。

    本次更新开始时,Fiber树存在如下2个fiber

    FiberRootNode
          |
      RootFiber      
     

    其中FiberRootNode是整个应用的根节点,RootFiber是调用ReactDOM.render创建的fiber

    首先,RootFiber会进入bailout的逻辑,所以返回的App fiber和更新前是一致的。

    FiberRootNode
          |
      RootFiber      
          |
      App fiber
     

    由于App fiberRootFiberbailout逻辑返回的,所以对于App fiberoldProps === newProps。并且bailout剩下3个条件也满足。

    所以App fiber也会走bailout逻辑,返回Parent fiber

    FiberRootNode
          |
      RootFiber      
          |
       App fiber
          |
     Parent fiber
     

    由于更新是Parent fiber触发的,所以他不满足条件4,会走render的逻辑。

    接下来是关键

    如果render返回的Son是如下形式:

    <Son/>
     

    会编译为

    React.createElement(Son, null)
     

    执行后返回JSX

    由于props的引用改变,oldProps !== newProps。会走render逻辑。

    但是在Demo中Son是如下形式:

    {props.children}
     

    其中,props.childrenSon对应的JSX,而这里的propsApp fiberbailout逻辑后返回的。

    所以Son对应的JSX与上次更新时一致,JSX中保存的props也就一致,满足条件1。

    可以看到,Son满足bailout的所有条件,所以不会render

     


     

    文章就分享到这,欢迎关注“前端大神之路” 

  • 相关阅读:
    C++17 filesystem文件系统
    简易版本vue的实现
    javaSE基础04
    javaSE基础03
    javaSE基础02
    JavaSE基础01
    Document对象和window对象
    进程和子进程及端口的常用命令
    vue分页组件二次封装---每页请求特定数据
    css图片垂直水平居中及放大(实现水平垂直居中的效果有哪些方法?)
  • 原文地址:https://www.cnblogs.com/cczlovexw/p/13901824.html
Copyright © 2020-2023  润新知