项目性能优化 React

最近项目迭代差不多,老板过来让我优化优化项目,什么优化,老板不会是想把我给优化掉吗,这不是难到我了嘛。但是也无奈只能硬着头皮搞,一把梭。

函数缓存

使用react项目嘛优化方案肯定也听的蛮多什么useCallback、useMemo、memo啊这些常规优化手段,但是使用这些优化手段之后发现页面还是很不流畅,复杂点的页面还是会重复渲染多次。

js 复制代码
const Son = React.memo(({ onChange }) => {
 
 return (
   <div onClick={onChange}>
    我爱加班、我爱加班!
   </div>
 )
})

const Parent = () => {
  const [value, setValue] = useState(0);
  
  const hanldeChange = useCallback(() => {
     const newValue = value + 5;
     setValue(newValue);
  }, [value]);
  
  // 一些复杂计算可以放入 这里只是案例,简单计算不用包裹useMemo ,并且useMemo缓存的是针对子组件的。
  const count = useMemo(() => {
    return value * 10000;
  }, [value]);
  
  return (
    <div>
      <p>喜提{ count }个bug</p>
      <button onClick={() => setValue(value++)}>我爱加班</button>
      <Son onChange={hanldeChange} />
    </div>
  )
  
}

从上面的案例可以看到 ,如果我们去改变value的值,那么对应的handleChange函数也就对应生成新的引用,那么这个时候Son组件也就会重新渲染,但是我们可以看到这里的Son组件只是想调用方法并不依赖父组件的展示数据,所以这里的重新渲染是没有必要的。

那么该如何处理这种情况呢,其实这里我们只需要保持handleChange的函数引用相同就行。

useMemoizedFn

js 复制代码
import { useMemo, useRef } from 'react';

type noop = (this: any, ...args: any[]) => any;

type PickFunction<T extends noop> = (
  this: ThisParameterType<T>,
  ...args: Parameters<T>
) => ReturnType<T>;

function useMemoizedFn<T extends noop>(fn: T) {
  const fnRef = useRef<T>(fn);

  fnRef.current = useMemo(() => fn, [fn]);

  const memoizedFn = useRef<PickFunction<T>>();
  if (!memoizedFn.current) {
    memoizedFn.current = function (this, ...args) {
      return fnRef.current.apply(this, args);
    };
  }

  return memoizedFn.current as T;
}

export default useMemoizedFn;

这里我们封装一个useMemoizedFn hook ,我们传入一个fn,把它存储在useRef中,当fn改变的时候做一个缓存,再创建一个memoizeFn来缓存我们的新建的方法,里面调用我们传入的函数,这里用单例设计模式第一次传的时候才会改变memoizefn的函数引用值,然后返回缓存函数。

这个时候我们回到页面上,可以看到我们改变value上的值的时候,Son组件没有重新渲染,符合预期结果。

缓存setValue

js 复制代码
const Son = React.memo(({ onChange }) => {
 
 return (
   <div onClick={() => onChange('加班到猝')}>
    我爱加班、我爱加班!
   </div>
 )
})

const Parent = () => {
  const [value, setValue] = useState('');

  
  return (
    <div>
      <p>喜提{ value }</p>
      <button onClick={() => setValue('加班')}>我爱加班</button>
      <Son onChange={setValue} />
    </div>
  )
  
}

上面案例中实际我们在父亲组件更改value的时候其实Son组件也重新加载了,这是因为更改value值导致组件更新对应的setValue的引用也发生了改变,所以造成了重复渲染,那么这里该如何处理呢。

js 复制代码
import { useCallback, useState } from 'react';

const useCallbackRefState = <T>() => {
  const [refState, setRefState] = useState<T | null>(null);
  const refCallback = useCallback((value: T) => setRefState(value), []);
  return [refState, refCallback];
};

export default useCallbackRefState;

这里我们使用一个简单的useCallback来缓存setValue方法,就可以到达Son组件不必要的渲染。

如何检测组件的更新的数据源

这里我推荐使用ahooks里面的一个很好用的hooks useWhyDidYouUpdate

js 复制代码
function Demo(props) {
  // 第一个参数 自定义一个组件名称 第二个参数就是想要检测的数据源
  useWhyDidYouUpdate('CustomCom', {
    ...props
  })
}

这个就能很好的检测哪些不必要的更新,来减少组件的重复渲染。

相关推荐
SuperEugene2 分钟前
Vue3 Props 传参实战规范:必传校验 + 默认值 + 类型标注,避开 undefined / 类型混用坑|Vue 组件与模板规范篇
前端·javascript·vue.js·前端框架
im_AMBER6 分钟前
万字长文:编辑器集成Vercel AI SDK
前端·人工智能·react.js·前端框架·编辑器
Y君8 分钟前
面了3个人后我发现:AI用得最溜的,未必是我最想要的工程师
前端·人工智能·面试
一拳不是超人9 分钟前
2026年最值得关注的JavaScript新特性:Signals,响应式编程的下一个十年
前端·javascript·响应式编程
skiy9 分钟前
Webpack、Vite区别知多少?
前端·webpack·node.js
Luna-player13 分钟前
npm install vue-awesome-swiper@5.0.1 swiper@7.4.1安装后,我又想全删了,怎么移除
前端·vue.js·npm
大雷神15 分钟前
HarmonyOS APP<玩转React>开源教程二十:收藏功能实现
前端·react.js·开源·harmonyos
晓得迷路了17 分钟前
栗子前端技术周刊第 121 期 - Vitest 4.1、Nuxt 4.4、Next.js 16.2...
前端·javascript·vite
kyle~18 分钟前
Electron桌面容器
前端·javascript·electron
隔壁小邓20 分钟前
vue如何拆分业务逻辑
前端·javascript·vue.js