React本身就非常关注性能,其提供的虚拟DOM搭配上DIff算法,实现对DOM操作最小粒度的改变也是非常高效的,然而其组件的渲染机制,也决定了在对组件更新时还可以进行更细致的优化。 

  

react组件渲染

  在讲react生命周期时,就谈到过react组件分为了初始化渲染和更新渲染, 初始化渲染会调用根组件下的所有组件的render方法进行渲染, 如下图所示(绿色表示已经渲染):

但是,当我们要更新某个组件的时候,如下面的绿色组件(从根组件传递下来应用在绿色组件上的数据发生变化

即在这三层中,只有最底下的一层中的某个绿色组件的数据发生变化,所以理想状态就是只调用关键路径组件上的render,如下图:

但是 react 的默认做法是调用所有组件的render再对生成的虚拟DOM进行比对,如果不变则不进行更新,这样的reader和虚拟DOM的对比是在浪费,如下图(黄色表示浪费的render和虚拟DOM的对比)

橘黄色 = 浪费的渲染

哦,不!我们所有的节点都被重新渲染了

注意: 每次更新的方式是: 如果状态发生改变,就先render,render的过程是生成虚拟DOM的过程,然后在拿这个虚拟DOM和上一次的虚拟DOM进行比对,生成一个patch,通过这个patch打到真实DOM上,得到更新,虽然,通过虚拟DOM,react很好的做到了DOM的更新很少,但是,在状态发生变化的时候,react在render的时候却整个(所有的组件)都render了,而不是render一部分(最好只render状态发生变化的那个组件),这样就造成了浪费。

那么如何才能避免发生这个浪费问题呢?  这里就要迁出我们的 shouldComponentUpdate 了。

  

shouldComponentUpdate

  在上面我们说到了在某一个组件更新的时候,只需要这一个组件调用render方法就可以了,而其他的组件不需要调用render方法(即使通过比对发现DOM没有改变,则不更新DOM,但是比对的时间也是被浪费了),那么怎么做到呢?实际上,react在每个组件生命周期更新的时候都会调用一个  shouldComponentUpdate(nextProps, nextState)函数, 他的职责就是返回true或false, true表示需要更新,而false表示不需要更新,默认返回true。 即使你没有显示的定义这个shouldComponentUpdate函数,那么返回的就是true,一定需要发生重新渲染,这样也就不安理解为什么会发生上面的资源浪费了  。

   为了避免一定程度的浪费,react官方还在0.14版本中添加了无状态组件,如下所示:

// es5
function HelloMessage(props) {
return <div>Hello {props.name}</div>;
}

   

// es6
const HelloMessage = (props) => <div>Hello {props.name}</div>;

  因为无状态组件压根就没有涉及到状态的改变,那么他们是不会参与到每次render和比对虚拟DOM的过程的,而是一旦创建,就稳如泰山了。

  

牛刀小试! 直接把一些不需要更新的组件返回false

比如,下面时一个音量图标,这是一个svg图标,不需要更新,所以在shouldComponentUpdate中直接return false, 如下:

import React, {Component} from 'react';

class Mic extends Component {
constructor(props) {
super(props);
}
shouldComponentUpdate() {
return false;
}
render() {
return (
<svg className="icon-svg icon-mic" xmlns="http://www.w3.org/2000/svg" width="" height="" viewBox="0 0 32 32" aria-labelledby="title">
<title>mic</title>
<path className="path1" d="M15 22c2.761 0 5-2.239 5-5v-12c0-2.761-2.239-5-5-5s-5 2.239-5 5v12c0 2.761 2.239 5 5 5zM22 14v3c0 3.866-3.134 7-7 7s-7-3.134-7-7v-3h-2v3c0 4.632 3.5 8.447 8 8.944v4.056h-4v2h10v-2h-4v-4.056c4.5-0.497 8-4.312 8-8.944v-3h-2z"></path>
</svg>
)
}
} export default Mic;

这样,无论上面的状态发生了怎样的改变,这里直接return false,所以就不会有render的过程了,避免了性能上的消耗和浪费。 其实,这样的情况直接使用无状态组件会更好,就不会有这个判断。 

登堂入室,对数据进行对比确定是否需要更新

先来个官网的例子,通过判断id是否改变来确定是否需要更新:

shouldComponentUpdate: function(nextProps, nextState) {
return nextProps.id !== this.props.id;
}

是不是很简单呢?但是是不是所有的都可以直接这么对比呢? 我们来看下js的两种数据类型的比较:

// 原始类型
var a = 'hello the';
var b = a;
b = b + 'world';
console.log(a === b); // false // 引用类型
var c = ['hello', 'the'];
var d = c;
d.push('world');
console.log(c === d); // true

我们可以看到 a 和 b 不等,但是c和d是相等的,因为对于引用类型,比较的时地址,这里的地址并乜有发生改变! 所以,我们得分情况处理了,原始数据类型和应用类型得采用不同的方法处理。 

原始数据类型: 

  原始数据的比对很简单,我们直接比对就是了,但是我们还可以更简单的比对, 因为每一个组件这么写下去也是挺麻烦的,于是react官方有了插件帮助我们搞定这件事: 

var PureRenderMixin = require('react-addons-pure-render-mixin');
React.createClass({
mixins: [PureRenderMixin], render: function() {
return <div className={this.props.className}>foo</div>;
}
});

  

var shallowCompare = require('react-addons-shallow-compare');
export class SampleComponent extends React.Component {
shouldComponentUpdate(nextProps, nextState) {
return shallowCompare(this, nextProps, nextState);
} render() {
return <div className={this.props.className}>foo</div>;
}
}

引用类型数据

  因为引用类型比对的是地址,所以可能一直都返回true,所以,我们得想办法吧前后的数据编程不一样的引用, 如下:

var update = require('react-addons-update');

var newData = update(myData, {
x: {y: {z: {$set: }}},
a: {b: {$push: []}}
});

  这样,newDate和myDate就可以比对了。

  • 直接改变引用
const newValue = {
...oldValue
// 在这里做你想要的修改
}; // 快速检查 —— 只要检查引用
newValue === oldValue; // false // 如果你愿意也可以用 Object.assign 语法
const newValue2 = Object.assign({}, oldValue); newValue2 === oldValue; // false

  

  Immutable Data 就是一旦创建,就不能再被更改的数据。对 Immutable 对象的任何修改或添加删除操作都会返回一个新的 Immutable 对象。

  Immutable 实现的原理是 Persistent Data Structure(持久化数据结构),也就是使用旧数据创建新数据时,要保证旧数据同时可用且不变。同时为了避免 deepCopy 把所有节点都复制一遍带来的性能损耗,Immutable 使用了 Structural Sharing(结构共享),即如果对象树中一个节点发生变化,只修改这个节点和受它影响的父节点,其它节点则进行共享, 这样,就是每次开辟了新的地址,但是其中的某个属性还是在对之前没有改变的数据作引用,这样的性能消耗,就会减少。请看下面动画:

其他的一些技巧:

  • 慎用setState,因为容易造成重新渲染。
  • 请将方法的bind一律置于constructor。
  • 请只传递component需要的props

使用Immutable.js

  immutable.js 可以很好的解决问题, 他的基本原则是对于不变的对象返回相同的引用,对于变化的对象,返回新的引用, 因此对于状态的比较只要使用如下代码:

 
shouldComponentUpdate() {
return ref1 !== ref2;
}

参考文章:

如何对react进行性能优化的更多相关文章

  1. React 组件性能优化

    React组件性能优化 前言 众所周知,浏览器的重绘和重排版(reflows & repaints)(DOM操作都会引起)才是导致网页性能问题的关键.而React虚拟DOM的目的就是为了减少浏 ...

  2. React Native 性能优化指南【全网最全,值得收藏】

    2020 年谈 React Native,在日新月异的前端圈,可能算比较另类了.文章动笔之前我也犹豫过,但是想到写技术文章又不是赶时髦,啥新潮写啥,所以还是动笔写了这篇 React Native 性能 ...

  3. React组件性能优化

    转自:https://segmentfault.com/a/1190000006100489 React: 一个用于构建用户界面的JAVASCRIPT库. React仅仅专注于UI层:它使用虚拟DOM ...

  4. React组件性能优化总结

    性能优化的思路 影响网页性能最大的因素是浏览器的重排(repaint)和重绘(reflow). React的Virtual DOM就是尽可能地减少浏览器的重排和重绘. 从React渲染过程来看,如何防 ...

  5. React项目性能优化

    1. 使用生产版本和Fragment 1. 生产版本 确保发布的代码是生产模式下(压缩)打包的代码. 一般运行npm run build命令. 直接从webpack看配置文件,需要设置mode = ' ...

  6. react教程 — 性能优化

    参考:https://segmentfault.com/a/1190000007811296?utm_medium=referral&utm_source=tuicool  或  https: ...

  7. react中性能优化的点

    react提升代码性能的点 1.绑定如果改变作用域点话放在constructor里面做,这样可以保证整个程序的作用域绑定操作只会执行一次,而且避免子组件的无谓渲染. 2.内置的setState是个异步 ...

  8. React 组件性能优化探索实践

    转自:http://www.tuicool.com/articles/Ar6Zruq React本身就非常关注性能,其提供的虚拟DOM搭配上Diff算法,实现对DOM操作最小粒度的改变也是非常的高效. ...

  9. react组件性能优化PureComponent

    首先我们使用react组件会配合connect来连接store获取state,那么只要store中的state发生改变组件就会重新渲染,所以性能不高,一般我们可以使用shouldComponentUp ...

随机推荐

  1. linux每天一小步---tail命令详解

    1 命令功能 tail命令用于显示文件中末尾的内容(默认显示最后10行内容) 2 命令语法 tail [选项参数] [文件名1] [文件名2] 3 命令参数 -f  用于循环读取文件的内容,监视文件的 ...

  2. Frames of Reference参考框架

    Frames of Reference参考框架 When describing the position and orientation of something (for example, your ...

  3. sun.misc.BASE64Decoder的风险

    问题描述 最近需要使用Base64上传图片,但是返现sun.misc.BASE64Decoder 为已经过期的包,此包为以前sun公司的内部包,可以下载此包,但是不利于现在Maven方式构建,可能会在 ...

  4. 用 pyqt4 编写的一个翻译小工具

    有时候我们在开发时遇到一些陌生的英文单词或者不容易看出某些长句的中文意思时该怎么办呢?打开桌面上的翻译软件?打开浏览器里收藏着的翻译网址或者直接贴上百度的搜索框去查?这些方法固然可以,还很常见,但如果 ...

  5. IOC和DI

    Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想.在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制(传统J ...

  6. 将图片流输出到界面mvc

    System.Drawing.Image _CodeImage = _Code39.GetCodeImage(OrderNo, Code39.Code39Model.Code39Normal, tru ...

  7. 浅议Github的注册和使用

    Self-introduction:编者本人叫司明周,现就读于南通大学计算机学院网络工程142班.爱好数学和音乐,喜欢数学中的逻辑性和天马行空的思维 编程能力:可以跳过略过得过且过吗..好吧,面对现实 ...

  8. Solr 从文件创建索引

    http://blog.csdn.net/clj198606061111/article/details/21492457 http://wiki.apache.org/solr/Extracting ...

  9. DOORS的引用类型

    DOORS的引用类型包括:Project: 项目引用Folder: 文件夹引用Item: 项目或文件夹之内的项(项目.文件夹.模块)Module: 打开模块的引用Object: 对象的引用 ...

  10. JS中移除非数字,最多保留一位小数

    //去除非数字 var clearNoNum = function (item) { if (item!=null && item!=undefined) { //先把非数字的都替换掉 ...