提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
react凭借 virtual DOM 和 diff算法 拥有高效的性能,除此之外也有很多其他的方法和技巧可以进一步提升react性能。一下几中方法有助于提升react性能,虽然不必一定要在项目中使用这些方法,但是我们有应该知道如何使用这些方法。
1、Reac.memo 缓存组件
React 中父组件每次更新都会导致子组件重新渲染,即使子组件的状态没有发生变化。
为了减少重复渲染,我们可以使用 React.memo来缓存组件,这样只有在传入组件的状态值发生变化时才会从新渲染。如果传入的值相同,则会返回缓存的组件。
示例:
// 使用 React.memo 将子组件包括起来const Child = React.memo((props)=>{
console.log('子组件');return<div>子组件</div>})constApp=()=>{
console.log('父组件');const[count, setCount]=useState(0);return(<><div>父组件:count:{count}</div><button onClick={()=>setCount(count +1)}>点击更新</button><Child /></>)}
2、使用 useMemo 缓存大量的计算
有时渲染是不可避免的,但如果您的组件是一个功能组件,重新渲染会导致每次都调用大型计算函数,这是非常消耗性能的,我们可以使用新的useMemo钩子来“记忆”这个计算函数的计算结果。这样只有传入的参数发生变化后,该计算函数才会重新调用计算新的结果。
通过这种方式,您可以使用从先前渲染计算的结果来挽救昂贵的计算耗时。总体目标是减少JavaScript在呈现组件期间必须执行的工作量,以便主线程被阻塞的时间更短。
示例:
// 避免这样做,每次传值都会调用 countFn 计算函数functionComponent(props){const res =countFn(props.item);return<div>{res}</div>}// 只有 props.item 改变时 res 的值才会被重新计算functionComponent(props){const res =useMemo(()=>countFn(props.item),[props.item]);return<div>{res}</div>}// 计算函数constcountFn=(item)=>{...}
3、避免使用 内联对象
使用内联对象时,react会在每次渲染时重新创建对此对象的引用,这会导致接收此对象的组件将其视为不同的对象。因此,该组件对于props的千层比较始终返回false,导致组件一直渲染。
示例:
// Don't do this!functionComponent(props){const aProp ={someProp:'someValue'}return<AComponent style={{margin:0}} aProp={aProp}/>}// Do this instead :)const styles ={margin:0};functionComponent(props){const aProp ={someProp:'someValue'}return<AComponent style={styles}{...aProp}/>}
4、避免使用 匿名函数
虽然匿名函数是传递函数的好方法,但它们在每次渲染上都有不同的引用。类似于内联对象。
为了保证作为props传递给react组件的函数的相同引用,如果使用的类组件可以将其声明为类方法,如果使用的函数组件,可以使用useCallback钩子来保持相同的引用。
示例:
// 避免这样做functionComponent(props){return<AComponent onChange={()=> props.callback(props.id)}/>}// 函数组件,优化方法一functionComponent(props){const handleChange =useCallback(()=> props.callback(props.id),[props.id]);return<AComponent onChange={handleChange}/>}// 类组件,优化方法二classComponentextendsReact.Component{handleChange=()=>{this.props.callback(this.props.id)}render(){return<AComponent onChange={this.handleChange}/>}}
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...</>}><HomeIndex /></React.Suspense>)// 一般会封装一个公共的方法constwithLoadingComponent=(comp:JSX.Element)=>(<React.Suspense fallback={<>Loading...</>}>{comp}</React.Suspense>)// 调用方法,传入要延迟加载的组件return({withLoadingComponent(<HomeIndex />})
6、调整CSS而不是强制组件加载和卸载
有时保持组件加载的同时,通过CSS隐藏可能是有益的,而不是通过卸载来隐藏。对于具有显著的加载或卸载时序的重型组件而言,这是有效的性能优化手段。
将元素透明度调整为0对浏览器的成本消耗几乎为0(因为它不会导致重排),并且应该尽可能优先更改visibility或display。
示例:
// 避免对大型的组件频繁对加载和卸载functionComponent(props){const[view, setView]=useState('view1');return view ==='view1'?<AComponent />:<BComponent />}// 使用该方式提升性能和速度const visibleStyles ={opacity:1};const hiddenStyles ={opacity:0};functionComponent(props){const[view, setView]=useState('view1');return(<React.Fragment><AComponent style={view ==='view1'? visibleStyles : hiddenStyles}><BComponent style={view !=='view1'? hiddenStyles : visibleStyles}></React.Fragment>)}
7、使用React.Fragment避免添加额外的DOM
有些情况下,我们需要在组件中返回多个元素,例如下面的元素,但是在react规定组件中必须有一个父元素。
<h1>Hello world!</h1><h1>Hello there!</h1><h1>Hello there again!</h1>
因此可能会这样做,但是这样做的话即使一切正常,也会创建额外的不必要的div。这会导致整个应用程序内创建许多无用的元素:
functionComponent(){return(<div><h1>Hello world!</h1><h1>Hello there!</h1><h1>Hello there again!</h1></div>)}
实际上页面上的元素越多,加载所需的时间就越多。为了减少不必要的加载时间,我们可以使React.Fragment来避免创建不必要的元素。
functionComponent(){return(<React.Fragment><h1>Hello world!</h1><h1>Hello there!</h1><h1>Hello there again!</h1></React.Fragment>)}
8、使用React.PureComponent , shouldComponentUpdate
父组件状态的每次更新,都会导致子组件的重新渲染,即使是传入相同props。但是这里的重新渲染不是说会更新DOM,而是每次都会调用diif算法来判断是否需要更新DOM。这对于大型组件例如组件树来说是非常消耗性能的。
在这里我们就可以使用React.PureComponent , shouldComponentUpdate生命周期来确保只有当组件props状态改变时才会重新渲染
示例:
exportdefaultfunctionParentComponent(props){return(<div><SomeComponent someProp={props.somePropValue}<div><AnotherComponent someOtherProp={props.someOtherPropValue}/></div></div>)}exportdefaultfunctionSomeComponent(props){return(<div>{props.someProp}</div>)}// 只要props.somePropValue 发生变化,不论props.someOtherPropValue是否发生变化该组件都会发生变化exportdefaultfunctionAnotherComponent(props){return(<div>{props.someOtherProp}</div>)}
9、除了以上八种还有很多
1、使用生产环境构建:使用生产环境构建工具如Webpack,可以进行代码压缩、混淆、分离等操作,减小文件大小,提高加载速度。
2、使用React.Fragment:使用React.Fragment来代替常用的div标签,可以减少不必要的DOM节点。
3、使用shouldComponentUpdate或React.memo:使用shouldComponentUpdate方法或React.memo函数来避免不必要的组件渲染,只有在props或state发生改变时才会重新渲染组件。
4、使用React.lazy和Suspense:使用React.lazy和Suspense来实现组件的懒加载,只在需要时才加载组件,减少初始加载时间。
5、使用PureComponent或React.memo:使用PureComponent类或React.memo函数来避免不要的组件渲染,只有在props或state发生改变时才会重新渲染组件。
6、使用key属性:在列表渲染时,为每个列表项添加唯一的key属性,可以帮助react更好地识别和更新组件。
7、使用虚拟化列表:对于长列表,可以使用虚拟化列表库如reacy-virtualized或react-window来减少DOM节点数量,提高性能。
8、使用Memoization:使用Memoization来缓存计算结果,避免重复计算,可以通过使用Memoize库或手动实现Memoization。
9、使用React DevTools:使用React DevTools工具可以帮助检查组件渲染性能,并进行性能分析和优化。
10、使用Web Workers:对于复杂地计算或大量数据处理,可以使用Web Workers在后台线程中进行处理避免阻塞主线程。
11、使用代码分割:将应用程序拆分为多个代码块,并在需要时动态加载,以减少初始加载时间。
12、使用组件的纯函数:将组件设计为纯函数,避免副作用和状态变化,可以更容易进行性能优化和测试。
13、避免不必要的重新渲染:避免在render函数中执行复杂的计算或操作,尽量将其移到组件的生命周期方法或其他地方执行。
14、使用Hooks:使用React Hooks可以更方便的管理组件的状态和副作用,减少组件的复杂度和重复代码。
版权归原作者 Mr.BoBo. 所有, 如有侵权,请联系我们删除。