React组件更新:探究两个典型案例

React组件更新:探究两个典型案例

React组件更新:探究两个典型案例

 
1 人赞同了该文章

案例一

有时候我们可能会看到这样的react 代码:

const Child1 = () => {
  return <div>Child1</div>;
};

function App() {
  const Child2 = () => {
    return <div>Child2</div>;
  };
  return (
    <div>
      <Child1 />
      <Child2 />
    </div>
  );
}

在过去,我傻傻地以为像 Child2 这样把组件定义在另一个组件内部和 Child1 没啥区别。但事实证明,这是大错特错!原来我们应该一直像 Child1 那样定义组件,因为 Child2 会招来巨大的性能麻烦。

当 App 组件更新时,会重新执行一次 App 函数。由于 App 函数内部每次都要新建一个 Child2,经过 Babel 编译后,它就成了 {type: Child2,…}。React 在组件更新时,会比较新旧节点的 type 是否相等,来决定是否复用老元素。然而,由于 type → Child2 永远是新函数,就算长得和老的一模一样,React 也会狠心地删除旧的 Child2,然后又创建一个一模一样的新 Child2

所以我们应该一直使用Child1 的方式来定义组件。

案例二

下面我们再看一段代码

function App() {
  return (
    <Child>
      <GrandChild />
    </Child>
  );
}

const GrandChild = () => {
  console.log('render-grand-child');
  return <div>GrandChild</div>;
};

const GrandChild1 = () => {
  console.log('render-grand-child1');
  return <div>GrandChild1</div>;
};

const Child = (props: any) => {
  const [count, setCount] = useState(0);
  const onClick = () => {
    setCount(count + 1);
  };
  return (
    <div onClick={onClick}>
      <h2>{count}</h2>
      {/* 通过children属性传入 */}
      {props.children}
      {/* 直接写到Child组件中 */}
      <GrandChild1 />
    </div>
  );
};

当 Child 组件更新时,GrandChild1 会重新执行,而 GrandChild 就好像没事发生一样。

我们看一下在babel编译以后的代码:

为了方便理解,我们将代码进行简化:

const jsx = (type,props) => {
    return {
            type,
            props
    }
}

const GrandChildElement = jsx(GrandChild,{})

// 省略其它无关元素
const Child = () => {
    return (
            {GrandChild1Element}
            jsx(GrandChild1,{})
    )
}

可以看到,每次 Child 重新执行时,都要重新创建一个新的 props 用于 GrandChild1,而 GrandChild 则复用了老的已创建好的 props。React 在组件更新时,会比对新旧节点的 props 是否相同,以决定是否重新执行函数组件。所以 GrandChild1 被重新执行,而 GrandChild 由于与老节点使用相同的 props,因此不会重新执行。

这俩个问题曾经困扰了我很久,最近在阅读React源码时,又再次重新理解了这些代码的执行细节。希望这些内容也能对你理解React代码有帮助

这俩问题曾困扰了我很久,直到最近我在研究 React 源码时,重新思考这俩个问题,感觉豁然开朗!希望这些内容也能给你在学习中带来一些启发。

posted on 2024-04-19 16:34  漫思  阅读(8)  评论(0编辑  收藏  举报

导航