别着急,坐和放宽
使用社交账号登录
距离我接触 react 已经过去几个月了,在此期间,关于如何避免重复渲染的问题一直困惑着我,因此今天就来聊聊这个话题。
在讲述如何进行性能优化之前,我们先来谈谈 React 为什么会重新渲染。
状态改变是 React 树内部发生更新的唯二原因之一。
import { useState } from "react";
const App = () => {
let [color, setColor] = useState("red");
return (
<div>
<input value={color} onChange={(e) => setColor(e.target.value)} />
<ExpensiveTree />
</div>
);
};
const ExpensiveTree = () => {
let now = performance.now();
while (performance.now() - now < 100) {
// 延迟
}
console.log('render');
return <p>I am a very slow component tree.</p>;
};

很明显,每当我们在 input 里面输入内容,console.log('render')都会输出,因为 color 的状态发生了改变,也间接说明了其与 props 完全没有关系
这样的开销是很不合理的,我们接下来会优化它。
我们知道 react 是单向数据流,因此我们只需要将 State 的抽离出来即可。
import { useState } from "react";
const App = () => {
return (
<div>
<Input />
<ExpensiveTree />
</div>
);
};
const ExpensiveTree = () => {
let now = performance.now();
while (performance.now() - now < 100) {
// 延迟
}
console.log("render");
return <p>I am a very slow component tree.</p>;
};
const Input = () => {
let [color, setColor] = useState("red");
return <input value={color} onChange={(e) => setColor(e.target.value)} />;
}
React.memo 其为高阶组件,可以使被它包裹的组件变为纯组件,也就是只要它的 prop 不改变,react 就不会更新它。
import { memo, useState } from "react";
const App = () => {
let [color, setColor] = useState("red");
return (
<div>
<input value={color} onChange={(e) => setColor(e.target.value)} />
<ExpensiveTree />
</div>
);
};
const ExpensiveTree = memo(() => {
let now = performance.now();
while (performance.now() - now < 100) {
// 延迟
}
return <p>I am a very slow component tree.</p>;
})
因为 App 并没有发生状态改变所以 ExpensiveTree 避免了重复渲染
import { FC, PropsWithChildren, useState } from "react";
const App = () => {
return (
<ColorWrapper>
<ExpensiveTree />
</ColorWrapper>
);
};
const ColorWrapper: FC<PropsWithChildren> = ({ children }) => {
let [color, setColor] = useState("red");
return (
<div>
<input value={color} onChange={(e) => setColor(e.target.value)} />
{children}
</div>
);
};
const ExpensiveTree = () => {
let now = performance.now();
while (performance.now() - now < 100) {
// 延迟
}
return <p>I am a very slow component tree.</p>;
};
useMemo 有点类似于 vue 中的 Computed,只有当依赖变化时,才会重新计算出新的值。
这样当 input 发生改变的时候 dirtyWork 就不会重复的去执行。
另外上一节的例子我们也可以通过 useMemo 进行修改
我们先看如下例子
我们发现即使 ExpensiveTree 包裹 memo ,但在 input 里面输入内容, ExpensiveTree 依旧会被更新,这时我们只要给父组件 fn 函数包裹一层 useCallback 即可
因此 useCallback 一般用于需要将函数传递给子组件的情况,我们用 useCallback 改写上面的例子:
你可能会发现 useCallback 其实就是 useMemo 的语法糖,如上例子也可以使用 useMemo 改写
import { useMemo, useState } from "react";
const App = () => {
let [color, setColor] = useState("red");
const [number,setNumber] = useState(0)
const dirtyWork = useMemo(() => {
console.log('正在进行大量运输');
return number
},[number])
return (
<div>
<input value={color} onChange={(e) => setColor(e.target.value)} />
<h1>{dirtyWork}</h1>
</div>
);
};
import { memo, useMemo, useState } from "react";
const App = () => {
let [color, setColor] = useState("red");
return (
<div>
<input value={color} onChange={(e) => setColor(e.target.value)} />
{useMemo(
() => (
<ExpensiveTree />
),
[]
)}
</div>
);
};
const ExpensiveTree = () => {
let now = performance.now();
while (performance.now() - now < 100) {
// 延迟
}
return <p>I am a very slow component tree.</p>;
};
import { FC, memo, useState } from "react";
const App = () => {
let [color, setColor] = useState("red");
const fn = ()=> {
console.log('hahaha');
}
return (
<div>
<input value={color} onChange={(e) => setColor(e.target.value)} />
<ExpensiveTree fn={fn}/>
</div>
);
};
const ExpensiveTree:FC<{fn:()=>void}> = memo(({fn}) => {
let now = performance.now();
while (performance.now() - now < 100) {
// 延迟
}
console.log('render'); // 依旧会被不断更新
return <p>I am a very slow component tree.</p>;
})
import { FC, memo, useCallback, useState } from "react";
const App = () => {
let [color, setColor] = useState("red");
const fn = useCallback(()=> {
console.log('hahaha');
},[])
return (
<div>
<input value={color} onChange={(e) => setColor(e.target.value)} />
<ExpensiveTree fn={fn}/>
</div>
);
};
const ExpensiveTree:FC<{fn:()=>void}> = memo(({fn}) => {
let now = performance.now();
while (performance.now() - now < 100) {
// 延迟
}
console.log('render');
return <p>I am a very slow component tree.</p>;
})
import { FC, memo, useMemo, useState } from "react";
const App = () => {
let [color, setColor] = useState("red");
const fn = useMemo(() => {
return () => console.log("hahaha");
}, []);
return (
<div>
<input value={color} onChange={(e) => setColor(e.target.value)} />
<ExpensiveTree fn={fn} />
</div>
);
};
const ExpensiveTree: FC<{ fn: () => void }> = memo(({ fn }) => {
let now = performance.now();
while (performance.now() - now < 100) {
// 延迟
}
console.log("render");
return <p>I am a very slow component tree.</p>;
});