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

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以及清晰的页面结构,不仅能提升性能,也能锻炼自己的设计能力。可能这就是俗称的 "计能" 吧

相关推荐
鹏北海6 分钟前
多标签页登录状态同步:一个简单而有效的解决方案
前端·面试·架构
_AaronWong11 分钟前
基于 Vue 3 的屏幕音频捕获实现:从原理到实践
前端·vue.js·音视频开发
孟祥_成都19 分钟前
深入 Nestjs 底层概念(1):依赖注入和面向切面编程 AOP
前端·node.js·nestjs
let_code20 分钟前
CopilotKit-丝滑连接agent和应用-理论篇
前端·agent·ai编程
Apifox44 分钟前
Apifox 11 月更新|AI 生成测试用例能力持续升级、JSON Body 自动补全、支持为响应组件添加描述和 Header
前端·后端·测试
木易士心1 小时前
深入剖析:按下 F5 后,浏览器前端究竟发生了什么?
前端·javascript
在掘金801101 小时前
vue3中使用medium-zoom
前端·vue.js
xump1 小时前
如何在DevTools选中调试一个实时交互才能显示的元素样式
前端·javascript·css
折翅嘀皇虫1 小时前
fastdds.type_propagation 详解
java·服务器·前端
Front_Yue1 小时前
深入探究跨域请求及其解决方案
前端·javascript