文章目录
- 1.避免使用内联对象
- 2.避免使用匿名函数
- 3.延迟加载不是立即需要的组件
- 4.调整CSS而不是强制组件加载和卸载
- 5.使用React.Fragment避免添加额外的DOM
- 6.使用React.PureComponent , shouldComponentUpdate
1.避免使用内联对象
使用内联对象时,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} />
}
2.避免使用匿名函数
虽然匿名函数是传递函数的好方法,但它们在每次渲染上都有不同的引用。类似于内联对象。
为了保证作为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 <AComponent onChange={handleChange} />
}// 类组件,优化方法二
class Component extends React.Component {handleChange = () => {this.props.callback(this.props.id) }render() {return <AComponent onChange={this.handleChange} />}
}
3.延迟加载不是立即需要的组件
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>
)// 一般会封装一个公共的方法
const withLoadingComponent = (comp: JSX.Element) => (<React.Suspense fallback={<>Loading...</>}>{comp}</React.Suspense>
)// 调用方法,传入要延迟加载的组件
return({withLoadingComponent(<HomeIndex />}
)
4.调整CSS而不是强制组件加载和卸载
有时保持组件加载的同时,通过CSS隐藏可能是有益的,而不是通过卸载来隐藏。对于具有显著的加载或卸载时序的重型组件而言,这是有效的性能优化手段。
将元素透明度调整为0对浏览器的成本消耗几乎为0(因为它不会导致重排),并且应该尽可能优先更改visibility或display。
// 避免对大型的组件频繁对加载和卸载
function Component(props) {const [view, setView] = useState('view1');return view === 'view1' ? <AComponent /> : <BComponent />
}// 使用该方式提升性能和速度
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>)
}
5.使用React.Fragment避免添加额外的DOM
有些情况下,我们需要在组件中返回多个元素,例如下面的元素,但是在react规定组件中必须有一个父元素。
<h1>Hello world!</h1><h1>Hello there!</h1><h1>Hello there again!</h1>
为了减少不必要的加载时间,我们可以使React.Fragment来避免创建不必要的元素。
function Component() {return (<React.Fragment><h1>Hello world!</h1><h1>Hello there!</h1><h1>Hello there again!</h1></React.Fragment>)
}
6.使用React.PureComponent , shouldComponentUpdate
父组件状态的每次更新,都会导致子组件的重新渲染,即使是传入相同props。但是这里的重新渲染不是说会更新DOM,而是每次都会调用diif算法来判断是否需要更新DOM。这对于大型组件例如组件树来说是非常消耗性能的。
在这里我们就可以使用React.PureComponent , shouldComponentUpdate生命周期来确保只有当组件props状态改变时才会重新渲染。
import React, { Component, PureComponent } from "react";// 没有变化不会触发render, 有变化触发render:
// 组件优化: 1.只要执行了setState, 都会触发render 2.触发render, 会render组件
// 只有当前state和props数据发生改变的话才触发render
// shouldComponentUpdate() 返回为 true
export default class Parent extends Component {state = { count: 0 };// 重写 shouldComponentUpdate()shouldComponentUpdate(nextProps, nextState){if(this.state.count === nextState.count) return falseelse return true}// 如果修改的对象和以前的对象有一点联系的话, 不会触发render, 比如就修改对象的名称, 不修改对象的地址render() {const { count } = this.state;return (<div className="parent">Parent<br />{count}<button onClick={() => this.setState({ count: 1 })}>1</button><Chird count={count} /></div>);}
}class Chird extends PureComponent {// shouldComponentUpdate(nextProps, nextState){// return !this.props.count === nextProps.count// }render() {return (<div className="child">Chird<br />{this.props.count}</div>);}
}