一、React的渲染机制

要掌握一两项React-render优化的方法不难,但是非常重要.无论是在实际项目中的一个小细节,还是迎合'面试官'的口味

1.1 触发Render

我们知道React要更新视图,必须要触发Render.而这往往是影响性能最重要的一步(因为操作了dom).而React之所以这么出色,正是因为占其主导地位的diff算法采用了虚拟DOM(React V-dom),使得渲染性能大大提升。

即便如此,我们在开发的时候也应该要注意一些性能的优化,比如避免无意义的render

那么,触发render的条件有哪些呢?

  1. 首次加载组件
  2. 使用了setState(更新了Props)
  3. Props更新了(父级传给子级的值改变了)

我们完全可以避免2.3情况导致的一些性能问题

1.2 React Diff

虽然说React高效的Diff算法完美结合了虚拟DOM,让用户可以'无限制'刷新而不需要考虑任何性能问题.

但diff算法还是需要一定的时间,如果你不在意触发render的细节,项目模块大了起来,自然就会影响性能了.

1.3 不做任何优化的例子

我尝试着从实现以下功能:

  1. 不更新state的值,但是调用了setState方法
  2. 传给子级的值不改变,即子级props实际上是没变化的
// 父级.js
import React from 'react'
import MockChild from './child/index.js'
export default class Demo5 extends React.Component{
constructor(args){
super(args) this.state = {
'mockNum': 0
}
}
handleClick(){
this.setState({
'mockNum': 0,
})
}
render(){
console.log('父级state ==============>', this.state)
return(
<div>
<button onClick={this.handleClick.bind(this)}>点击</button>
<MockChild mockNum={this.state.mockNum}/>
</div>
)
}
} //子组件
render(){
console.log('子级Props =============>', this.props)
return(
<div></div>
)
}

我们反复点击按钮,虽然state的值并没有任何变化,但是我们看打印的结果!

render重复渲染了!

可能会疑问,我在项目并不会做这么一种无用功的!但实际上,当一个组件逻辑复杂起来之后,会产生各种各样的情况.比如:

比如一个组件的中有个包含onChange事件的input,当我改变该输入框内容的时候调用了setState,渲染视图的时候也会触发子组件的重新render.但我们明明没有做任何和子组件有联系的操作

例如上面的例子:

//父组件.js
state = {
'mockValue': '123'
}
handleChange(e){
this.setState({
'value': '123',
})
} //render
<input onChange={this.handleChange.bind(this)} />
<MockChild /> /*
* 子组件不做变化
*/

很不爽,真的!必须给安排掉.

二、基础的React优化

2.1 生命周期: shouldComponentUpdate

可能没听过shouldComponentUpdate,我们简单介绍一下它的执行周期

不熟悉React生命周期的可以看看这篇文章:《React生命周期执行顺序详解》

可以看到它是在render渲染之前触发的,只要我们在这里加以判断就可以有效阻止'无用'的render触发.当然你说componentWillReceiveProps也可以,当然!但是它只有props的更新判断,而有时候我们也不能放过未更改的state!

shouldComponentUpdate(nextProps, nextState){
if(nextState !== this.state && nextProps !== this.props) return true;
return false
}

这么简短的代码就可以解决冗余的render触发.当然有时候项目规模大了之后,就需要具体到某一个值

nextState.xxx !== this.state.xxx && ...

2.2 PureComponent

先介绍PureComponent的用法,实在是太简便了

import React, { PureComponent } from 'react'

export default class Demo5 extends PureComponent{

}

实际上就是把React.Component代替成PureComponent.

可能会疑问那PureComponent应该是一个插件吧?为什么就在react包里?其实它是官方在React15.3提出的一个'纯净的组件'

在版本允许的情况下,还是建议使用PureComponent,既能优化render次数,也能减少shouldComponentUpdate的代码。但是PureComponent只是执行了浅比较

什么是浅比较呢?

我们先来看看源码

if (this._compositeType === CompositeTypes.PureClass) {
shouldUpdate = !shallowEqual(prevProps, nextProps)
|| !shallowEqual(inst.state, nextState);
}

可以看到判断主要是通过shallowEqual方法执行的(即可以判断state,也可以判断props)

shallowEqual具体作用是什么呢?实际上它使用了ES6的Object.keys.只是做了以下这么几个判断:

  1. 新的和旧的props(state)是否两者都有相同的key?
  2. 引用是否改变

第二种可能有点难以理解,什么是引用是否改变?

简单地解释就是,是否有新的元素参与改变

举个官方常用例子:

class App extends PureComponent {
state = {
items: [1, 2, 3]
}
handleClick = () => {
const { items } = this.state;
items.pop();
this.setState({ items });
}
render() {
return (<div>
<ul>
{this.state.items.map(i => <li key={i}>{i}</li>)}
</ul>
<button onClick={this.handleClick}>delete</button>
</div>)
}
}

可以看到,我们点击delete的时候,虽然items数组执行了pop()方法,删除最后一项!但是li标签却没变少!那是因为shallowEqual根本不吃你这套.它认为items还是同一个引用,所以给我true!在通过!反过来就是false

那要如果改变引用呢?

我们可以这样尝试.(这也是解决浅比较常用的一个办法)

this.setState({
items: [].concat(items)
});

当然,大多数情况我们都可以通过PureComponent解决啦!实在不行,还可以通过componentWillReceiveProps进一步判断呢!

PureComponent更多的介绍可以看:《React PureComponent 使用指南》

你所要掌握的最简单基础的React渲染优化的更多相关文章

  1. 2.1实现简单基础的vector

    2.1实现简单基础的vector 1.设计API 我们参考下C++ <std> 库中的vector, vector中的api很多,所以我们把里面用的频率很高的函数实现; 1.1 new&a ...

  2. java:Spring框架1(基本配置,简单基础代码模拟实现,spring注入(DI))

    1.基本配置: 步骤一:新建项目并添加spring依赖的jar文件和commons-logging.xx.jar: 步骤二:编写实体类,DAO及其实现类,Service及其实现类; 步骤三:在src下 ...

  3. 02 . Vue入门基础之条件渲染,列表渲染,事件处理器,表单控件绑定

    vue基础 声明式渲染 Vue.js 的核心是一个允许采用简洁的模板语法来声明式地将数据渲染进 DOM 的系统 Example1 <!DOCTYPE html> <html> ...

  4. java学习之路--简单基础的面试题

    1.面向对象的特征有哪些方面? 答:面向对象的特征主要有以下几个方面: 1)抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面.抽象只关注对象有哪些属性和行为,并不关注 ...

  5. java 学习第一篇简单基础

    Java基础 Java Java 和C#有着极为相似的语法. 和C#都是面向对象的高级程序语言. JAVA是一个开源,公开的语言,有着极其丰富的开源库和其他资源. JAVA分类 JAVA分SE EE ...

  6. ADB简单基础命令

    1.查看设备 adb devices 这个命令是查看当前连接的设备, 连接到计算机的android设备或者模拟器将会列出显示 2.安装软件 adb install adb install :这个命令将 ...

  7. java IO文件操作简单基础入门例子,IO流其实没那么难

    IO是JAVASE中非常重要的一块,是面向对象的完美体现,深入学习IO,你将可以领略到很多面向对象的思想.今天整理了一份适合初学者学习的简单例子,让大家可以更深刻的理解IO流的具体操作. 1.文件拷贝 ...

  8. jQuery的一些简单基础知识

    ### 什么是jQuery?jQuery(js+Query)是一款优秀的JavaScript库,帮助开发人员用最少的代码做更多的事情,官网网站http://jquery.com/ ### 为什么学习j ...

  9. unity网络----简单基础

    网络 TCP:与打电话类似,通知服务到位 UDP:与发短信类似,消息发出即可 IP和端口号是网络两大重要成员 端口号(Port)分为知名端口号[0-1024,不开放)和动态端口号[1024,10000 ...

随机推荐

  1. 迭代器 生成器 yield

    iter 迭代iterable 可迭代的 iterator迭代器 dir函数查看一个数据类型内部含有哪些方法 两边带着双下划线的方法叫做"魔术方法","双下方法" ...

  2. 计蒜客---N的-2进制表示

    对于十进制整数N,试求其-2进制表示. 例如,因为  1*1  +  1*-2  +  1*4  +  0*-8  +1*16  +  1*-32  =  -13  ,所以(-13)_10  =  ( ...

  3. EF使用sql语句

    https://www.cnblogs.com/chenwolong/p/SqlQuery.html https://blog.csdn.net/zdhlwt2008/article/details/ ...

  4. 高级架构进阶之HashMap源码就该这么学

    引言--面试常见的问题 问:“你用过HashMap,你能跟我说说它吗?” “当然用过,HashMap是一种<key,value>的存储结构,能够快速将key的数据put方式存储起来,然后很 ...

  5. Docker学习笔记之Docker的Build 原理

    0x00 概述 使用 Docker 时,最常用的命令无非是 docker container 和 docker image 相关的子命令,当然最初没有管理类命令(或者说分组)的时候,最常使用的命令也无 ...

  6. PHP 变量类型的强制转换 & 创建空对象

    PHP 在变量定义中不需要(或不支持)明示的类型定义:变量类型是根据使用该变量的上下文所决定的. 也就是说,如果把一个字符串值赋给变量 var,var 就成了一个字符串.如果又把一个整型值赋给 var ...

  7. Vue基础进阶 之 Vue生命周期与钩子函数

    Vue生命周期 Vue生命周期:Vue实例从创建到销毁的过程,称为Vue的生命周期: Vue生命周期示意图:https://cn.vuejs.org/v2/guide/instance.html#生命 ...

  8. Linux进程内存分析和内存泄漏定位

    在Linux产品开发过程中,通常需要注意系统内存使用量,和评估单一进程的内存使用情况,便于我们选取合适的机器配置,来部署我们的产品. Linux本身提供了一些工具方便我们达成这些需求,查看进程实时资源 ...

  9. lasticsearch -2.3.x head插件安装(windows系统)

    1.进入命令行: home键+R 命令: > cd \elasticsearch根目录\bin\ > plugin  install  mobz/elasticsearch-head 2. ...

  10. DL_WITH_PY系统学习(第2章)

    ​ 本节提示: 1.第一个dl例子: 2.tensor和tensor操作: 3.DL如何通过逆向传播和梯度下降达到学习目的. 2.1 输入数据集的格式 ,*,))) network.add(layer ...