资讯专栏INFORMATION COLUMN

React学习之前端开发性能优化

channg / 3132人阅读

摘要:单个组件性能优化里面尽量减少新建变量和函数的使用,尽量减少传递参数的数量在中绑定函数,无非就是下面三种第一种是在构造函数中绑定,第二种是在函数里面绑定,第三种就是使用箭头函数,上述方法都能实现的绑定。

1. 单个react组件性能优化 1.1 render里面尽量减少新建变量和bind函数的使用,尽量减少传递参数的数量

render中绑定函数,无非就是下面三种:

render() {
  return (
    
1 2 this.handleClick()}>3
) }

第一种是在构造函数中绑定this,第二种是在render()函数里面绑定this,第三种就是使用箭头函数,上述方法都能实现this的绑定。

但是哪一种方法的性能最好,是我们要考虑的问题。毫无疑问第一种的性能最好

第一种方法,构造函数每渲染一次便会执行一遍;

第二种方法,在每次render()的时候都会重新执行一遍函数;

第三种方法,每一次render()的时候,都会生成一个新的箭头函数,即使两个箭头函数的内容是一样的。

react判断是否需要进行render浅层比较,简单来说就是通过===来判断的,如果state或者prop的类型是字符串或者数字,只要值相同,那么浅层比较就会认为其相同;

但是如果前者的类型是复杂的对象的时候,我们知道对象是引用类型,浅层比较只会认为这两个prop是不是同一个引用,如果不是,哪怕这两个对象中的内容完全一样,也会被认为是两个不同的prop

举个例子:

当我们给组件App名为styleprop赋值;


使用这种方法,每一次渲染都会被认为是一个style这个prop发生了变化,因为每一次都会产生一个对象给style

如果想要让react渲染的时候认为前后对象类型prop相同,则必须要保证prop指向同一个javascript对象,改进如下:

const appStyle = { color: "red" }; //这个初始化只执行一次,不要放在render中,可以放在构造函数中
1.2 定制shouldComponentUpdate函数

生命周期函数shouldComponentUpdate是决定react组件什么时候能够重新渲染的函数,但是这个函数默认的实现方式就是简单的返回一个true。也就是说,默认每次更新的时候都要调用所用的生命周期函数,包括render函数,重新渲染。

看看下面这个例子:

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count = 2,
      name = "apple",
    }
    this.handleClick = this.handleClick.bind(this);
    this.handleName = this.handleName.bind(this);
  }
  this.handleClick() {
  // ...
  }
  this.handleName() {
  // ...
  }
  render() {
    return (
      
数量,{this.state.count}
); } } class Child extends React.Component { render() { console.log("render了一次"); return (

我想吃,{this.props.title}

); } }

我们写了两个组件,AppChild组件,并写两个方法,一个改变App中的count的值,一个是改变name,我们在Childrender中打印了每次是否执行。

不出意外,虽然Child组件里的title值没有改变,但是还是render了。

为了进一步优化这个问题,我们这样改Child组件:

class Child extends React.Component {
  shouldComponentUpdate(nextProps,nextState) {
    if(nextProps.title == this.props.title) {
      return false;
    }
    return true;
  }

  render() {
    console.log("render了一次");
    return (
      

我想吃,{this.props.title}

); } }

只有当Childtitle值发生改变的时候,组件才会去render

在最新的react中,react给我们提供了React.PureComponent,官方也在早期提供了名为react-addons-pure-render-mixin插件来重新实现shouldComponentUpdate生命周期方法。

class Child extends React.PureComponent {
  // shouldComponentUpdate(nextProps,nextState) {
  //   if(nextProps.title == this.props.title) {
  //     return false;
  //   }
  //   return true;
  // }

  render() {
    console.log("render了一次");
    return (
      

我想吃,{this.props.title}

); } }

通过上述的方法的效果也是和我们先前定制shouldComponentUpdate的效果是一致的。

但是我们要注意的是,这里的PureRender是浅比较的,因为深比较的场景是相当昂贵的。所以我们要注意我们在1.1中说到的一些注意点:不要直接为props设置对象或者数组不要将方法直接绑定在元素上,因为其实函数也是对象。

1.3 Immutable.js

javascript中的对象一般都是可变的,因为使用了引用赋值,新的对象简单的引用了原始对象,改变新对象将影响到原始对象。

举个例子:

student = { age : 1 };
school = student;
school.age = 2;

当我们给school.age赋值后,会发现student.a也变成了2,虽然我们可以通过深拷贝与浅拷贝解决这个问题,但是这样做非常的昂贵,对cpu和内存会造成浪费。

这里就需要用到Immutable,通过Immutable创建的Immutable Data一旦被创建,就不能再更改。对Immutable对象进行修改、添加或删除操作,都会返回一个新的Immutable对象。

下面是三个比较重要且用到的数据结构

Map:键值对集合,对应Object,ES6中也有专门的Map对象

List:有序可重复列表,对应于Array

ArraySet:有序且不可重复的列表

我们可以看一个例子:

使用Map生成一个immutable对象:

import { Map, is } from "immutable";

let a = Map({
  "name": "apple",
  "list": Map({name: "orange"})
})

let b = a.set("name","banana");

console.log(a.get("course") === b.get("course")); // 返回true
console.log(a === b); // 返回false

Immutable.is 比较的是两个对象的 hashCodevalueOf(对于JavaScript对象)。由于immutable内部使用了Trie数据结构来存储,只要两个对象的 hashCode 相等,值就是一样的。这样的算法避免了深度遍历比较,性能非常好。

Immutable优点

减少内存的使用

并发安全

降低项目的复杂度

便于比较复杂数据,定制shouldComponentUpdate方便

时间旅行功能

函数式编程

Immutable缺点

学习成本

库的大小(建议使用seamless-immutable

对现有项目入侵严重

容易与原生的对象进行混淆

2. 多个react组件性能优化

react组件在装载过程中,react通过在render方法在内存中产生一个树形结构,树上的节点代表一个react组件或者原生的Dom元素,这个树形结构就是我们所谓的Vitural Domreact根据这个来渲染产生浏览器的Dom树。

react在更新阶段对比原有的Vitural Dom和新生成的Vitural Dom,找出不同之处,在根据不同来渲染Dom树。

react为了追求高性能,采用了时间复杂度为O(N)来比较两个属性结构的区别,因为要确切比较两个树形结构,需要通过O(N^3),这会降低性能。

节点类型不同

// A组件
// B组件

我们想把A组件更新成B组件,react在做比较的时候,发现最外面的根结点完全不一样,直接销毁之前的

节点,包括里面的子节点也一并销毁,这是一个巨大的浪费,但是为了避免O(N^3)的时间复杂度,只能采用这种方式。

所以在开发过程中,我们应该尽量避免上面的情况,不要将包裹节点的类型随意改变。

两个节点类型一样

这里包括两种情况,一种是节点是Dom类型,还有一种react组件。

对于dom类型,我们举个例子:

// A组件
Hello World!!!
// B组件
Good Bye!!!

上述A和B组件的区别是文字、classNamestyle中的color发生改变,因为Dom元素没变,React只会修改他变化的部分。

针对react组件类型,渲染无非就是再执行一遍组件实例的更新过程,最主要的就是定制shouldComponentUpdate

多个子组件情况

例子一:

// A
// B

从A变到B,如果shouldComponentUpdate处理得当,我们只需要更新装载third的那一次就行。

我们来看看下一个例子:

// A
// B

这里因为react是采用O(n)的时间复杂度,所以会依次将textFirst的改为ZerotextSecond改为First,在最后再加上一个组件,textSecond。现存的两个的text的属性都被改变了,所以会依次渲染。

如果我们这里有100个实例,那么就会发生100次更新。

这里我们就要用到Key

简单来说,其实这一个Key就是react组件的身份证号。

我们将上一个例子改成如下,就可以避免上面的问题了,react就能够知道其实B里面的第二个和第三个组件其实就是A中的第一个和第二个实例。

// A
// B

不过现在,react也会提醒我们不要忘记使用key,如果没有加,浏览器中会报错。

关于key的使用我们要注意的是,这个key值要稳定不变的,就如同身份证号对于我们是稳定不变的一样。

一个常见的错误就是,拿数组的的下标值去当做key,这个是很危险的,代码如下,我们一定要避免

    { todos.map((item, index) => { }) }

未完待续...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/105573.html

相关文章

  • 前端每周清单第 48 期:Slack Webpack 构建优化,CSS 命名规范与用户追踪,Vue.

    摘要:发布是由团队开源的,操作接口库,已成为事实上的浏览器操作标准。本周正式发布,为我们带来了,,支持自定义头部与脚部,支持增强,兼容原生协议等特性变化。新特性介绍日前发布了大版本更新,引入了一系列的新特性与提升,本文即是对这些变化进行深入解读。 showImg(https://segmentfault.com/img/remote/1460000012940044); 前端每周清单专注前端...

    sean 评论0 收藏0
  • 前端每周清单第 34 期:Vue 现状盘点与 3.0 展望,React 代码迁移与优化,图片优化详论

    摘要:工程实践立足实践,提示实际水平内联函数与性能很多关于性能优化的文章都会谈及内联函数,其也是常见的被诟病为拖慢性能表现的元凶之一不过本文却是打破砂锅问到底,论证了内联函数并不一定就会拖慢性能,过度的性能优化反而会有损于应用性能。 showImg(https://segmentfault.com/img/remote/1460000011481413?w=1240&h=825); 前端每周...

    CoderStudy 评论0 收藏0
  • 王下邀月熊_Chevalier的前端每周清单系列文章索引

    摘要:感谢王下邀月熊分享的前端每周清单,为方便大家阅读,特整理一份索引。王下邀月熊大大也于年月日整理了自己的前端每周清单系列,并以年月为单位进行分类,具体内容看这里前端每周清单年度总结与盘点。 感谢 王下邀月熊_Chevalier 分享的前端每周清单,为方便大家阅读,特整理一份索引。 王下邀月熊大大也于 2018 年 3 月 31 日整理了自己的前端每周清单系列,并以年/月为单位进行分类,具...

    2501207950 评论0 收藏0
  • 前端每周清单第 41 期 : Node 与 Rust、OpenCV 的火花,网络安全二三事

    摘要:的网站仍然使用有漏洞库上周发布了开源社区安全现状报告,发现随着开源社区的日渐活跃,开源代码中包含的安全漏洞以及影响的范围也在不断扩大。与应用安全是流行的服务端框架,本文即是介绍如何使用以及其他的框架来增强应用的安全性。 showImg(https://segmentfault.com/img/remote/1460000012181337?w=1240&h=826); 前端每周清单专注...

    syoya 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<