【微笔记】好的编码习惯对应好的React性能

打个广告:招web前端、java开发、移动端ios开发、协同办公产品经理
职责:协同办公类产品
公司: 京东
Base:北京亦庄
邮箱:zhangpengcheng15@jd.com
简历直推对应负责人,筛选过了直接面试。

React组件分为两种,类组件和函数式组件。

类组件

当一个组件的 props 或 state 变更,React 会将最新返回的元素与之前渲染的元素进行对比,以此决定是否有必要更新真实的 DOM。当它们不相同时,React 会更新该 DOM。

不要在render方法中做大资源消耗的计算

即使 React 只更新改变了的 DOM 节点,重新渲染仍然花费了一些时间。在做虚拟DOM的计算中,react调用了类组件中的render方法,重新生成了虚拟DOM树,此处为第一个优化点

有时,我们会在render函数里,对现有数据进行处理,才能生成渲染所需数据,如下所示

render(){
    const { list } = this.state;
    cosnt splitList = computer(list); // 做拆分以及一些其他消耗资源的处理
    return <div>/****内容****/</div>;
}
复制代码

每一次由于父组件的更新或者组件本身state的变化导致的渲染,都会消耗大量时间来做数据计算,导致页面卡顿。因此,类似的数据计算应该在数据变更的初始阶段去做,或者对该计算做缓存,例如使用一些memo数据的库。

覆盖生命周期方法 shouldComponentUpdate 

该方法会在重新渲染前被触发。其默认实现总是返回 true,让 React 执行更新。

如果你知道在什么情况下你的组件不需要更新,你可以在 shouldComponentUpdate 中返回 false 来跳过整个渲染过程。其包括该组件的 render 调用以及之后的操作。

如果你的组件只有当 props.color 或者 state.count 的值改变才需要更新时,你可以使用 shouldComponentUpdate 来进行检查:

shouldComponentUpdate(nextProps, nextState) {
    if (this.props.color !== nextProps.color) {
      return true;
    }
    if (this.state.count !== nextState.count) {
      return true;
    }
    return false;
  }
复制代码

使用 PureComponent而不是Component

React.PureComponent 与 React.Component很相似。两者的区别在于 React.PureComponent以浅层对比 prop 和 state 的方式实现了 shouldComponentUpdate()

使用 PureComponent需要注意的一点是,其父组件变更state时,需要使用新的对象,而不是直接变更原对象。 因为对于引用数据类型,如果地址没有改变,PureComponent会认为props没有发生变化。

对于数组,ES6 数组支持扩展运算符,这让代码写起来更方便了。

handleClick() {
  this.setState(state => ({
    words: [...state.words, 'marklar'],
  }));
};
复制代码

对于对象,为了不改变原本的对象,我们可以使用 Object.assign 方法:

Object.assign({}, colormap, {right: 'blue'});
复制代码

这也是React推荐的方式。

在construct中做bind绑定

为了解决函数中的this指向问题,有人使用bind来绑定,这种方法,推荐在construce中 this.func = this.func.bind(this),而不是在onclick中onClick={this.func.bind(this)}。后面这种方式,每次渲染都会绑定新的函数到onclick中。

另一种推荐方法是直接使用箭头函数,即在定义时直接定义箭头函数func = () => {},不推荐onClick={()=>this.func()},除非函数需要传参。

函数式组件

函数式组件本身是一个函数,每次父组件更新或着自身state发生变化时,该函数都会被执行一次,与类组件只执行render函数不同,函数式组件的函数会从头到尾执行一次。

不要在函数中执行大资源消耗的计算

虽然每次渲染,函数组件会从头执行到尾,但是Hook 不会因为在渲染时创建函数而变慢。在现代浏览器中,闭包和类的原始性能只有在极端场景下才会有明显的差别。

我们需要避免的只是不要在每次的执行过程中加入大资源消耗的计算。如果有,使用useMemo来进行缓存。

使用useCallback包裹内部函数

传统上认为,在 React 中使用内联函数对性能的影响,与每次渲染都传递新的回调会如何破坏子组件的 shouldComponentUpdate 优化有关。

[useCallback] Hook 允许你在重新渲染之间保持对相同的回调引用以使得 shouldComponentUpdate 继续工作。

另外,使用useCallback包裹的函数,在该组件内部进行绑定时,可以避免每次渲染导致的事件重新绑定。因为每次渲染过程中,生成的子函数都是新的函数,react需要将其重新绑定到对应组件的事件上。

使用 React.memo

既然类组件有PureComponent,函数式组件自然也有对应的操作。

如果你的组件在相同 props 的情况下渲染相同的结果,那么你可以通过将其包装在 React.memo 中调用,以此通过记忆组件渲染结果的方式来提高组件的性能表现。

const MyComponent = React.memo(function MyComponent(props) {
  /* 使用 props 渲染 */
});
复制代码

PureComponent类似,默认情况下其只会对复杂对象做浅层对比,如果你想要控制对比过程,那么请将自定义的比较函数通过第二个参数传入来实现。

function MyComponent(props) {
  /* 使用 props 渲染 */
}
function areEqual(prevProps, nextProps) {
  /*
  如果把 nextProps 传入 render 方法的返回结果与
  将 prevProps 传入 render 方法的返回结果一致则返回 true,
  否则返回 false
  */
}
export default React.memo(MyComponent, areEqual);
复制代码

与 class 组件中 [shouldComponentUpdate()] 方法不同的是,如果 props 相等,areEqual 会返回 true;如果 props 不相等,则返回 false。这与 shouldComponentUpdate 方法的返回值相反。

需要注意的是,即使函数组件被 React.memo 包裹,如果其实现中拥有 [useState],[useReducer] 或 [useContext] 的 Hook,当 state 或 context 发生变化时,它仍会重新渲染。

点个赞再走吧~

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享