Nextjs渲染性能优化常见方法
AI摘要: 本文详细介绍了Next.js中常见的渲染性能优化方法,包括使用useCallback缓存函数创建以避免子组件不必要的重新渲染、利用useMemo缓存复杂计算结果减少重复运算、合理配置useEffect的依赖数组控制副作用执行时机,以及正确使用Suspense实现异步组件加载时的友好等待提示。这些技术手段能有效提升React应用的性能表现。
useCallback
背景:当函数组件重新渲染的时候,会重新执行整个函数体,导致里面定义的函数会被重新创建:
function MyComponent() {
const handleClick = () => {
console.log("clicked");
};
return <button onClick={handleClick}>点我</button>;
}
-
每次组件重新渲染,创建的
handleClick
都是新的函数对象 -
重新创建函数对象一般消耗不多,没什么关系
-
但是如果这个函数被传递给了子组件,函数的变化会导致子组件的重新渲染,消耗很大
const Child = React.memo(({ onClick }) => {
console.log("Child render");
return <button onClick={onClick}>子组件按钮</button>;
});
function Parent() {
const [count, setCount] = useState(0);
const handleClick = () => console.log("clicked");
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>加1</button>
<Child onClick={handleClick} />
</div>
);
}
-
Child组件依赖
onClick
函数,handleClick
的重新创建,导致了Child组件的重新渲染 -
解决办法:缓存函数创建
const handleClick = useCallback(() => {
console.log("clicked");
}, []);
- 只有当
[]
中的内容发生变化时候,才会重新生成新的函数
useMemo
上面讲到,函数重新创建一般消耗不大,但是如果函数计算逻辑复杂,函数计算也是可能消耗很大的, 那么可以用useMemo来缓存函数计算结果
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
-
useMemo缓存一个计算结果
-
只有依赖项
[a, b]
发生变化时,才会重新计算 -
避免了复杂逻辑重新计算
useEffect
函数组件渲染的时候,不仅仅渲染JSX,还需要进行一些其他操作(数据获取等)
-
useEffect(() =>{...})
: 每次渲染结束都执行 -
useEffect(() =>{...}, [])
:只有第一次渲染结束才执行 -
useEffect(() =>{...}, [a, b])
: 只要a或者b发生变化,渲染结束都执行
useEffect(() => {
const handleResize = () => console.log(window.innerWidth);
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []); // 空数组 → 组件挂载时注册,卸载时清理
-
函数体是渲染结束之后做的事情
-
渲染出来的组件如果被卸载也要做一些事情,可以通过return封装
Suspense
用来异步渲染:
// app/page.tsx
import ResourceList from './ResourceList';
export default function Page() {
return (
<Suspense fallback={<div>Loading resources...</div>}>
<ResourceList />
</Suspense>
);
}
// app/ResourceList.tsx
async function ResourceList() {
const resources = await fetch('https://api.example.com/resources').then(res => res.json());
return (
<ul>
{resources.map(r => <li key={r.id}>{r.title}</li>)}
</ul>
);
}
-
只有被包裹的组件是异步组件才有用
-
<Suspense>
内部直接放<div>
没啥用(之前一直都用错了)