使用内联对象时,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} />
}
虽然匿名函数是传递函数的好方法,但它们在每次渲染上都有不同的引用。类似于内联对象。
为了保证作为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} />
}
}
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 />}
)
有时保持组件加载的同时,通过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>
)
}
有些情况下,我们需要在组件中返回多个元素,例如下面的元素,但是在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>
)
}
父组件状态的每次更新,都会导致子组件的重新渲染,即使是传入相同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 false
else 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>
);
}
}