“醉”简单的性能优化解释

React这玩意天生就是直肠子,从一个节点开始,以及后续的子组件,当触发状态改变的时候,不管子组件有没有用父组件这老东西,他都要更新一遍,以至于在diff对比这块速度是比不上Vue,vue的依赖收集机制,可以知道知道更新的数据,以及触发对应 的组件渲染,那么react在更新组件这块就依赖于咱手动来控制,控制什么呢,

  • Props

坡破死,这玩意在日常开发过程中是用的最多的,编写受控组件、页面交互基本离不开他。我们直接用最简单的模板做测试

javascript 复制代码
// APP.tsx
function App() {
    console.log(count, "App函数执行");
  useEffect(() => {
    console.log(count, "APP挂载");
  }, []);
  useEffect(() => {
    console.log(count, "APPcount变化");
  }, [count]);
  const [count, setCount] = useState(0);
  return (
    <>
      <h1>Vite + React</h1>
      <div className="card">
        <button onClick={() => setCount((count) => count + 1)}>count is {count}</button>
      </div>
      <Test></Test>
    </>
  );
}
javascript 复制代码
import React from "react";
export const Test = ({ count, onChange }) => {
  console.log(111, "Test组件执行");
  useEffect(() => {
    console.log(222, "Test组件挂载");
  }, []);

  return <div>Test</div>;
};

可以点击count 加之后,我的Test组件明明没有用到父组件的Count 变量还是被重新渲染了,这是为嘛呢

  1. react 的更新由于state count值发生变化,导致触发了函数重新渲染
  2. 那么这玩意是个函数啊,当他执行到Test组件的时候,就又会去重新执行一遍Test函数,所以人家又执行一遍啊,没毛病

可是... 雪啊 飘进双眼..

咳咳!

但是咱都是有追求的人,高质量程序搬砖师,婶能忍,叔不能忍啊,所以第一招,就是memo啊,这函数怎么念起来有点emo

memo

我们在test组件上包一层memo

javascript 复制代码
export const TestMemo = React.memo(() => {
  console.log(111, "Test组件执行");

  useEffect(() => {
    console.log(222, "Test组件挂载");
  }, []);

  return <div>test</div>;
});

此时count值更新后,就没有test组件渲染的事啦

那emo,做了啥这么牛逼?

在没emo前,test的props相当于也是 {} ,那么每次渲染的时候 {} === {} 是返回false 的,所以react又重新渲染了,加了之后,会进行一层浅比较,为什么是浅呢,我猜是因为效率吧,所以如果你的props里又函数之类的,用了memo 也得用usecallback ,来保证每次函数渲染的时候返回的引用是一样的

useCallback

举个例子

我setCount 包一个函数传递下去,

javascript 复制代码
//app.tsx
 <TestMemo onChange={() => setCount((count) => count + 1)}></TestMemo>

// test.tsx
export const TestMemo = React.memo(({  onChange }) => {
  console.log(111, "Test组件执行");

  useEffect(() => {
    console.log(222, "Test组件挂载");
  }, []);

  return <div onClick={onChange}>test1</div>;
});

里面点击的时候调用onChange,来触发count更新 组件再次渲染。

看onchange每次都不一样,所以test组件还是又被无辜的渲染了,解决这个问题,就是包一层useCallback咯

javascript 复制代码
 const onChange = useCallback(() => setCount((count) => count + 1), []);

<TestMemo onChange={onChange}></TestMemo>

这样就不会再次渲染了

useMemo

这玩意跟useCallback,只不过他缓存的是值,可以用来缓存组件,如果一个组件有100条数据呢,渲染一次可多费劲啊

javascript 复制代码
//Test,tsx
export const Test = ({ onChange }) => {
  console.log(111, "Test组件执行");
  useEffect(() => {
    console.log(222, "Test组件挂载");
  }, []);
  return (
    <>
      {new Array(10000).fill(1).map((item, index) => {
        return (
          <div onClick={onChange} key={index}>
            Test{index}
          </div>
        );
      })}
    </>
  );
};
javascript 复制代码
  const TestUsemo = useMemo(() => {
    return <Test onChange={()=>void} ></Test>;
  }, []);

如果不包裹,那么每次Test渲染都要经历1000次的重新渲染, 这样包裹一个组件,只要deps不发生变化,组件/值就不会渲染。

页面结构解决

编写清晰的页面结构可以解决 性能优化问题,将变的抽离出来 ,让他自己变去,不影响到自组件

javascript 复制代码
import { useEffect, useState, useCallback, useMemo, memo } from "react";
import reactLogo from "../assets/react.svg";
import viteLogo from "/vite.svg";
const CountComponent: React.FC<object> = (props: React.PropsWithChildren<object>) => {
  const [count, setCount] = useState(0);
  console.log("CountComponent渲染", count);
  useEffect(() => {
    console.log("count变化", count);
  }, [count]);
  return (
    <>
      <div className="card">
        <button onClick={() => setCount((count) => count + 1)}>count is {count}</button>
        <p>
          Edit <code>src/App.tsx</code> and save to test HMR
        </p>
      </div>
      <p className="read-the-docs">Click on the Vite and React logos to learn more</p>
      {props.children}
    </>
  );
};
const Children = () => {
  console.log("子组件渲染");
  return (
    <>
      <div>我是子组件</div>
    </>
  );
};
function Test1() {
  return (
    <>
      <div>
        <a href="https://vitejs.dev" target="_blank">
          <img src={viteLogo} className="logo" alt="Vite logo" />
        </a>
        <a href="https://react.dev" target="_blank">
          <img src={reactLogo} className="logo react" alt="React logo" />
        </a>
      </div>
      <h1>Vite + React</h1>
      <CountComponent>
        <Children></Children>
      </CountComponent>
    </>
  );
}

export default Test1;

这里没有用任何什么 hooks包裹,但是父组件CountComponent重新渲染后,并没有引入子组件的变化,这里的props.children,内部应该也是做了优化。

所以将变和不变的结构分离出来,比无脑使用各种hooks,性能更好。react在业务中使用,我们更应该去设计合理的props以及清晰的页面结构,不仅能提升性能,也能锻炼自己的设计能力。可能这就是俗称的 "计能" 吧

相关推荐
放逐者-保持本心,方可放逐几秒前
vue3 中那些常用 靠copy 的内置函数
前端·javascript·vue.js·前端框架
IT古董几秒前
【前端】vue 如何完全销毁一个组件
前端·javascript·vue.js
Henry_Wu0013 分钟前
从swagger直接转 vue的api
前端·javascript·vue.js
SameX12 分钟前
初识 HarmonyOS Next 的分布式管理:设备发现与认证
前端·harmonyos
M_emory_39 分钟前
解决 git clone 出现:Failed to connect to 127.0.0.1 port 1080: Connection refused 错误
前端·vue.js·git
Ciito42 分钟前
vue项目使用eslint+prettier管理项目格式化
前端·javascript·vue.js
成都被卷死的程序员1 小时前
响应式网页设计--html
前端·html
mon_star°2 小时前
将答题成绩排行榜数据通过前端生成excel的方式实现导出下载功能
前端·excel
Zrf21913184552 小时前
前端笔试中oj算法题的解法模版
前端·readline·oj算法
文军的烹饪实验室3 小时前
ValueError: Circular reference detected
开发语言·前端·javascript