lazy
lazy 能够让你在组件第一次被渲染之前延迟加载组件的代码。
注:不要在其他组件内部声明lazy组件(将导致在重新渲染时重置所有状态):
import { lazy } from 'react';
function Editor() {
// 🔴 Bad: 这将导致在重新渲染时重置所有状态
const MarkdownPreview = lazy(() => import('./MarkdownPreview.js'));
// ...
}
相反,总是在模块的顶层声明它们:
import { lazy } from 'react';
// ✅ Good: 将 lazy 组件声明在组件外部
const MarkdownPreview = lazy(() => import('./MarkdownPreview.js'));
function Editor() {
// ...
}
import { lazy } from 'react';
const MarkdownPreview = lazy(() => import('./MarkdownPreview.js'));
// 用<Suspense>标签包裹,在加载过程中可以加载 loading 组件
<Suspense fallback={<Loading />}>
<MarkdownPreview />
</Suspense>
MarkdownPreview 的代码只有在你尝试渲染它时才会被加载。如果 MarkdownPreview 还没有加载完成,将显示 Loading
import { useState, Suspense, lazy } from 'react';
import Loading from './Loading.js';
const MarkdownPreview = lazy(() => delayForDemo(import('./MarkdownPreview.js')));
export default function MarkdownEditor() {
const [showPreview, setShowPreview] = useState(false);
const [markdown, setMarkdown] = useState('Hello, **world**!');
return (
<>
<textarea value={markdown} onChange={e => setMarkdown(e.target.value)} />
<label>
<input type="checkbox" checked={showPreview} onChange={e => setShowPreview(e.target.checked)} />
Show preview
</label>
<hr />
//此处进行加载
{showPreview && (
<Suspense fallback={<Loading />}>
<h2>Preview</h2>
<MarkdownPreview markdown={markdown} />
</Suspense>
)}
</>
);
}
// 添加一个固定的延迟时间,以便你可以看到加载状态
function delayForDemo(promise) {
return new Promise(resolve => {
setTimeout(resolve, 2000);
}).then(() => promise);
}
memo
(记忆化只与从父组件传递给组件的props有关。)
memo 允许你的组件在 props 没有改变的情况下跳过重新渲染
例:
import { memo, useState } from 'react';
export default function MyApp() {
const [name, setName] = useState('');
const [address, setAddress] = useState('');
return (
<>
<label>
Name{': '}
<input value={name} onChange={e => setName(e.target.value)} />
</label>
<label>
Address{': '}
<input value={address} onChange={e => setAddress(e.target.value)} />
</label>
<Greeting name={name} />
</>
);
}
①memo包裹前的组件
此处没有包裹 memo 所以不管是 name 变化还是 address 变化 都会导致 Greeting 组件重新渲染
function Greeting({ name }) {
console.log("Greeting was rendered at", new Date().toLocaleTimeString());
return <h3>Hello{name && ', '}{name}!</h3>;
};
②memo包裹后的组件 作为对比
Greeting组件在name 更改时重新渲染(因为那是它的 props 之一),
但是在address更改时不会重新渲染(因为它不作为 props 传递给Greeting)
const Greeting = memo(function Greeting({ name }) {
console.log("Greeting was rendered at", new Date().toLocaleTimeString());
return <h3>Hello{name && ', '}{name}!</h3>;
});
如果传递给组件的 props 始终不同,例如在渲染期间传递对象或普通函数,则 memo 是完全无用的。这就是为什么你通常需要在 memo 中同时使用 useMemo 和 useCallback。
1.即使一个组件被(memo)记忆化了,当它自身的(组件内部定义的 state)状态发生变化时,它仍然会重新渲染。memoization 只与从父组件传递给组件的 props 有关。
2.即使组件已被记忆化,当其使用的 context 发生变化时,它仍将重新渲染。记忆化只与从父组件传递给组件的 props 有关。
memo的第二个参数用于自己定义比较参数的方法(react的比较是浅层的)
const Chart = memo(function Chart({ dataPoints }) {
// ...
}, arePropsEqual);
function arePropsEqual(oldProps, newProps) {
return (
oldProps.dataPoints.length === newProps.dataPoints.length &&
oldProps.dataPoints.every((oldPoint, index) => {
const newPoint = newProps.dataPoints[index];
return oldPoint.x === newPoint.x && oldPoint.y === newPoint.y;
})
);
}
useMemo
useMemo 是一个 React Hook,它在每次重新渲染的时候能够缓存计算的结果。
useCallback
useCallback 是一个允许你在多次渲染中缓存函数的 React Hook