React 函数式组件性能优化相关技术点

主要围绕以下四点展开:Memo、Fragment、Lazy、Suspense

1. memo高阶组件函数

memo 是一个高阶组件(Higher-Order Component)函数,用于优化函数组件的性能。它的作用是对函数组件进行浅比较,减少不必要的重新渲染。
memo会对组件的props进行浅比较(判断新的prop和旧的prop引用是否相等),从而决定在其父组件重新渲染时该组件是否重新渲染。

React官方文档将memo包装起来的组件称为获得该组件的一个记忆化版本。记忆化是一种性能优化,而非保证。

javascript 复制代码
import React, { memo } from 'react';

// 使用 memo 包裹组件
const MyComponent = memo(({ name, age }) => {
  console.log('MyComponent rendered');

  return (
    <div>
      <h2>Name: {name}</h2>
      <p>Age: {age}</p>
    </div>
  );
});

function App() {
  const [count, setCount] = React.useState(0);

  return (
    <div>
      <h1>Count: {count}</h1>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <MyComponent name="John" age={30} />
    </div>
  );
}

在上面这个示例中,当点击Increment按钮时,count状态会更新,导致App组件重新渲染。但由于MyComponent的props没有发生变化,memo会跳过重新渲染MyComponent。

由此可推出memo的好处:
1. 性能优化:通过避免不必要的重新渲染,提高应用程序的整体性能。
2. 减少不必要的计算:如果组件的props没有发生变化,则不需要重新计算组件的输出。
3. 节省内存和CPU资源:减少不必要的渲染可以节省内存和CPU资源的使用,提高应用程序的整体效率。

当然,memo的用法不止这一种。还可以给memo函数传入第二个参数,该参数是一个函数,且接收两个参数:组件的旧props和新props。你可以自定义比较两个props(React默认使用Object.is进行比较),同时返回一个布尔值。true表示不重新渲染,false反之。

2. Fragment 组件

Fragment 是一个特殊的组件,它允许我们将多个元素包裹在一起,而不会在 DOM 树中产生额外的节点。

主要用途包括以下几点:
1. 避免额外的DOM节点

  • 通常情况下,React 组件必须返回一个单一的根元素。
  • 使用 Fragment 可以让组件返回多个元素,而不会在 DOM 树中产生额外的节点。
    2. 提高DOM结构的可读性
  • 在某些情况下,组件返回多个兄弟元素会使 DOM 结构变得更加清晰。
  • 使用 Fragment 可以将这些元素包裹在一起,提高代码的可读性和可维护性。
    3. 性能优化
  • 由于 Fragment 不会在 DOM 树中产生额外的节点,因此使用 Fragment 可以提高应用程序的性能。
  • 特别是在需要大量渲染子元素的情况下,使用 Fragment 可以避免不必要的 DOM 操作。
javascript 复制代码
import React, { Fragment } from 'react';

function MyComponent() {
  return (
    <Fragment>
      <h1>Hello</h1>
      <p>World</p>
    </Fragment>
  );
}

// 或者使用短语法
function MyComponent() {
  return (
    <>
      <h1>Hello</h1>
      <p>World</p>
    </>
  );
}

除以上基本用法外,Fragment可以拥有key属性,这就表示也可以在循环中使用该组件。

javascript 复制代码
function MyList() {
  const items = [
    { id: 1, value: 'Item 1' },
    { id: 2, value: 'Item 2' },
    { id: 3, value: 'Item 3' },
  ];

  return (
    <Fragment>
      {items.map((item) => (
        <Fragment key={item.id}>
          <h3>{item.value}</h3>
          <p>Some content</p>
        </Fragment>
      ))}
    </Fragment>
  );
}

3. Lazy函数

lazy函数用于实现组件的动态导入,可以将组件的定义推迟到需要渲染时再进行加载,从而优化应用的初始加载性能。动态导入也就是常说的懒加载。

javascript 复制代码
import { lazy } from 'react';

const MarkdownPreview = lazy(() => import('./MarkdownPreview.js'));

lazy传的参数是一个返回 Promise 或另一个 thenable(具有 then 方法的类 Promise 对象)的函数。React 不会在你尝试首次渲染返回的组件之前调用 load 函数。在 React 首次调用 load 后,它将等待其解析,然后将解析值的 .default 渲染为 React 组件。返回的 Promise 和 Promise 的解析值都将被缓存,因此 React 不会多次调用 load 函数。如果 Promise 被拒绝,则 React 将抛出拒绝原因给最近的错误边界处理。

lazy函数返回一个 React 组件,你可以在 fiber 树中渲染。当懒加载组件的代码仍在加载时,尝试渲染它将会处于 暂停 状态。使用 可以在其加载时显示一个正在加载的提示。

javascript 复制代码
import React, { lazy, Suspense } from 'react';

const MyComponent = lazy(() => import('./MyComponent'));

function App() {
  return (
    <div>
      <h1>My App</h1>
      <Suspense fallback={<div>Loading...</div>}>
        <MyComponent />
      </Suspense>
    </div>
}

4. Suspense组件

Suspense 是一个 React 组件,用于处理异步加载导致的组件渲染中断。

当使用 React.lazy 动态加载组件时,如果组件还未加载完成,Suspense 组件会显示一个回退 UI(fallback)。

Suspense 还可以用于处理其他类型的异步操作,如数据获取、图像预加载等。

javascript 复制代码
import React, { lazy, Suspense } from 'react';

const resourceCache = new Map();

function fetchData(key) {
  if (!resourceCache.has(key)) {
    const promise = fetch(`/data/${key}`)
      .then((response) => response.json());
    resourceCache.set(key, promise);
  }
  return resourceCache.get(key);
}

function DataResource(props) {
  const resource = fetchData(props.id);
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <Data resource={resource} />
    </Suspense>
  );
}

function Data({ resource }) {
  const data = await resource;
  return <div>{data.name}</div>;
}

function App() {
  return (
    <div>
      <h1>My App</h1>
      <DataResource id="123" />
    </div>
  );
}

使用 Lazy 和 Suspense 的主要优点包括:

1. 代码分割: 可以实现组件的动态导入,减少初始加载时的文件大小。
2. 异步渲染: 可以处理异步加载导致的组件渲染中断,提供良好的用户体验。
3. 错误处理: Suspense 可以与 errorBoundary 配合使用,提供更完整的错误处理方案。

相关推荐
aPurpleBerry1 小时前
JS常用数组方法 reduce filter find forEach
javascript
ZL不懂前端1 小时前
Content Security Policy (CSP)
前端·javascript·面试
乐闻x1 小时前
ESLint 使用教程(一):从零配置 ESLint
javascript·eslint
我血条子呢2 小时前
[Vue]防止路由重复跳转
前端·javascript·vue.js
半开半落2 小时前
nuxt3安装pinia报错500[vite-node] [ERR_LOAD_URL]问题解决
前端·javascript·vue.js·nuxt
理想不理想v3 小时前
vue经典前端面试题
前端·javascript·vue.js
小阮的学习笔记3 小时前
Vue3中使用LogicFlow实现简单流程图
javascript·vue.js·流程图
YBN娜3 小时前
Vue实现登录功能
前端·javascript·vue.js
阳光开朗大男孩 = ̄ω ̄=3 小时前
CSS——选择器、PxCook软件、盒子模型
前端·javascript·css