主要围绕以下四点展开: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 配合使用,提供更完整的错误处理方案。