react凭借 virtual DOM和 diff算法拥有高效的性能,除此之外也有很多其他的方法和技巧可以进一步提升react性能。一下几中方法有助于提升react性能,虽然不必一定要在项目中使用这些方法,但是我们有应该知道如何使用这些方法。 1 Reac.memo 缓存组件 提示应用程序性能的一种方法是实现 memoization。Memoization 是一种优化技术,主要通过昂贵的函数调用结果,并在再次发生相同的输入时返回缓存的结果,以此来加速程序。
React 中父组件每次更新都会导致子组件重新渲染,即使子组件的状态没有发生变化。
为了减少重复渲染,我们可以使用 React.memo来缓存组件,这样只有在传入组件的状态值发生变化时才会从新渲染。如果传入的值相同,则会返回缓存的组件。
示例:
// 使用 React.memo 将子组件包括起来 const Child = React.memo((props) => { console.log('子组件'); return
const App = () => { console.log('父组件'); const [count, setCount] = useState(0); return(<>
通过这种方式,您可以使用从先前渲染计算的结果来挽救昂贵的计算耗时。总体目标是减少JavaScript在呈现组件期间必须执行的工作量,以便主线程被阻塞的时间更短。
// 避免这样做,每次传值都会调用 countFn 计算函数 function Component(props) { const res = countFn(props.item); return
// 只有 props.item 改变时 res 的值才会被重新计算 function Component(props) { const res = useMemo(() => countFn(props.item), [props.item]); return
// 计算函数 const countFn = (item) => { ... }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 3 避免使用 内联对象 使用内联对象时,react会在每次渲染时重新创建对此对象的引用,这会导致接收此对象的组件将其视为不同的对象。因此,该组件对于props的千层比较始终返回false,导致组件一直渲染。
// Don't do this!
function Component(props) {
const aProp = { someProp: 'someValue' }
return <AComponent style={{ margin: 0 }} aProp={aProp} />
}
// Do this instead :)
const styles = { margin: 0 };
function Component(props) {
const aProp = { someProp: 'someValue' }
return <AComponent style={styles} {...aProp} />
}
1 2 3 4 5 6 7 8 9 10 11 12 13 4 避免使用 匿名函数 虽然匿名函数是传递函数的好方法,但它们在每次渲染上都有不同的引用。类似于内联对象。
为了保证作为props传递给react组件的函数的相同引用,如果使用的类组件可以将其声明为类方法,如果使用的函数组件,可以使用useCallback钩子来保持相同的引用。
// 避免这样做
function Component(props) {
return <AComponent onChange={() => props.callback(props.id)} />
}
// 函数组件,优化方法一
function Component(props) {
const handleChange = useCallback(() => props.callback(props.id), [props.id]);
return
}
// 类组件,优化方法二 class Component extends React.Component { handleChange = () => { this.props.callback(this.props.id) } render() { return } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 5 延迟加载不是立即需要的组件 使用React.lazy和React.Suspense完成延迟加载不是立即需要的组件。React加载的组件越少,加载组件的速度越快。
// React.lazy 接受一个函数,这个函数需要动态调用 import()引入组件 const HomeIndex = React.lazy(() => import('@/modules/home')) ......
// 然后应在 Suspense 组件中渲染 lazy 组件,如此使得我们可以使用在等待加载 lazy 组件时做优雅降级(如 loading 指示器等) return( <React.Suspense fallback={<>Loading...</>}> </React.Suspense> )
// 一般会封装一个公共的方法 const withLoadingComponent = (comp: JSX.Element) => ( <React.Suspense fallback={<>Loading...</>}> {comp} </React.Suspense> )
// 调用方法,传入要延迟加载的组件 return( {withLoadingComponent(} ) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 6 调整CSS而不是强制组件加载和卸载 有时保持组件加载的同时,通过CSS隐藏可能是有益的,而不是通过卸载来隐藏。对于具有显著的加载或卸载时序的重型组件而言,这是有效的性能优化手段。
将元素透明度调整为0对浏览器的成本消耗几乎为0(因为它不会导致重排),并且应该尽可能优先更改visibility或display。
// 避免对大型的组件频繁对加载和卸载
function Component(props) {
const [view, setView] = useState('view1');
return view === 'view1' ? :
}
// 使用该方式提升性能和速度 const visibleStyles = { opacity: 1 }; const hiddenStyles = { opacity: 0 }; function Component(props) { const [view, setView] = useState('view1'); return ( <React.Fragment> <AComponent style={view === 'view1' ? visibleStyles : hiddenStyles}> <BComponent style={view !== 'view1' ? hiddenStyles : visibleStyles}> </React.Fragment> ) } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 7 使用React.Fragment避免添加额外的DOM 有些情况下,我们需要在组件中返回多个元素,例如下面的元素,但是在react规定组件中必须有一个父元素。
<h1>Hello world!</h1>
<h1>Hello there!</h1>
<h1>Hello there again!</h1>
1 2 3 因此可能会这样做,但是这样做的话即使一切正常,也会创建额外的不必要的div。这会导致整个应用程序内创建许多无用的元素:
function Component() { return (
Hello world!
Hello there!
Hello there again!
function Component() { return ( <React.Fragment>
Hello world!
Hello there!
Hello there again!
</React.Fragment> ) } 1 2 3 4 5 6 7 8 9 8 使用React.PureComponent , shouldComponentUpdate 父组件状态的每次更新,都会导致子组件的重新渲染,即使是传入相同props。但是这里的重新渲染不是说会更新DOM,而是每次都会调用diif算法来判断是否需要更新DOM。这对于大型组件例如组件树来说是非常消耗性能的。 在这里我们就可以使用React.PureComponent , shouldComponentUpdate生命周期来确保只有当组件props状态改变时才会重新渲染。如下例子:export default function ParentComponent(props) { return (
export default function SomeComponent(props) { return (
) }
// 只要props.somePropValue 发生变化,不论props.someOtherPropValue是否发生变化该组件都会发生变化 export default function AnotherComponent(props) { return (
) }