从 React 的组件更新谈 Immutable 的应用
在介绍 Immutable 如何在 React 中应用之前,先来谈谈 React 组件是如何更新的。
React 是基于状态驱动的开发,可以将一个组件看成是一个有限状态机,组件要更新,必须更新状态。
通常说的组件的状态就是组件的 state 对象,state 是可以由当前组件自行修改更新的,这种自更新的状态的为了便于理解区分可以称之为“动态”的状态。但除了更新 state 外,组件还可以通过 props 来更新,props 属性不能由组件自行修改,必须由父组件来修改,然后再传递给当前组件,更新组件的 props也能引起组件的更新,可以将 props 称之为“静态”的状态。这样的状态区分是广义上的,如果你不认可 props 也是状态也没关系,这里可以不用拘泥于文字。
组件的更新说起来可能会显得抽象一点,实际上我们说要更新一个组件其实就是更新 DOM 树,React 设计的再牛逼,要在浏览器中跑起来最终还是要生成 DOM 树。在使用 React 时通常并不直接操作 DOM,而是由状态驱动,状态可以随时更新,而 DOM 树并不会随着状态的更新而更新,因为对于 DOM 的频繁的操作是很耗费性能的。为了节省性能,React 内部实现了一套 Virtual DOM (虚拟的 DOM),简言之就是将原生的 DOM 树通过 JavaScript 对象来做一次映射。状态的更新会引起 Virtual DOM 的更新,React 通过高效的 Diff 算法来比较状态更新前和更新后的 Virtual DOM 是否真的变化,只有 Virtual DOM 更新了才会更新真实的 DOM 树。

只要状态更新,那么就一定会引起 Virtual DOM 的 Diff 操作,而原生 DOM 是否更新就要看 Diff 的结果。
如果要更新状态,无论是动态的 state 状态,还是静态的 props 状态,必然要主动调用 setState 方法,而且只要调用了该方法,那么就一定会更新状态。这也意味着每调用一次 setState 方法都会触发 Virtual DOM 的 Diff 操作,尽管可能并未更新原生 DOM,Diff 操作也会带来性能的开销。
每更新一次状态,组件都会执行 Update 的生命周期中的一系列方法:
componentWillReceivePropsshouldComponentUpdatecomponentWillUpdatecomponentDidUpdate
当然,还会执行 render 方法。
不过实际情况还要复杂些,只要一个组件更新了动态的 state 状态,那么这个组件包含的所有子组件以及子组件一层层嵌套的子组件都会执行上面的 Update 流程。

关于 setState 方法,调用之后并不会立即执行,而是会有一个事件循环,在这个循环中标记组件为 dirty 状态,等事件循环结束,才会将所有标记为 dirty 状态的组件执行 Update 的流程,此时才会重新 render 所有的子孙组件。所以 setState 是一个异步的操作。
React 的这种组件的更新方式,虽然用了很多办法来节省性能开销,诸如事件循环的机制、Virtual DOM、高性能的 Diff 算法,但仍然避免不了会有一些性能开销,并且随着组件的复杂度的提升对于性能的开销而成正比。
那么有没有办法对这种更新方式进行优化呢?先来看看 React 都提供了什么。在 Update 的生命周期阶段会触发一个 shouldComponentUpdate 的方法,在这个方法中可以主动的去 Diff 状态。
shouldComponentUpdate (nextProps, nextState) {
return nextProps.id !== this.props.id;
};
shouldComponentUpdate 方法在执行完如果返回的是 true,那么组件就会继续 Update 的流程,如果返回 false,则不会继续 Update 的流程,默认情况下都是返回的 true。上面的方法判断的是两次的 id 是否相等,如果不相等才继续 Update 流程。
有没有觉得这样挺好的,好像有哪里不对啊,组件的状态我不可能都以硬编码的形式写上一大坨而且无法复用的代码,而且说好的 Immutable 也没见用上啊。
是的,上面说了那么一大堆,总算该轮到 Immutable 出场了。在进行状态的 Diff 时,对于复杂的 Mutable 数据,一项一项的去遍历不现实,借用 Immutable,可以直接实现「值」的比较,而且性能又好。
所有复杂的状态的 Diff,结合 Immutable,都能用下面这个工具方法全搞定。
import { is } from 'immutable';
const keys = Object.keys;
const shallowEqualImmutable = (context, nextProps, nextState) => {
const currentState = context.state;
const currentProps = context.props;
const nextStateKeys = keys(nextState || {});
const nextPropsKeys = keys(nextProps);
// 先从数据的长度判断
if (nextStateKeys.length !== keys(currentState || {}).length ||
nextPropsKeys.length !== keys(currentProps).length
) {
return true;
}
// 再按key逐个比较数据是否相等
let isUpdate = nextStateKeys.some((item) => (
currentState[item] !== nextState[item] &&
!is(currentState[item], nextState[item])
));
if (isUpdate) {
return true;
}
return nextPropsKeys.some((item) => (
currentProps[item] !== nextProps[item] &&
!is(currentProps[item], nextProps[item])
));
};
那么组件中的 state 或 props 状态数据,对于 Mutable 类型的数据都相应的都要转换成 Immutable 数据。
// 组件的初始 state 定义
state = {
$list: Immutable.List([1, 2, 3]),
foo: 'bar'
}; // 在某个方法中调用 setState
doSomething = () => {
let $list = this.state.$list;
$list = $list.push(4); this.setState({ $list });
}; // 调用 Diff 的工具方法
shouldComponentUpdate (nextProps, nextState) {
return shallowEqualImmutable(this, nextProps, nextState);
};
将组件的 Mutable 状态都转换成 Immutable 后,组件的 Update 流程会变成如下所示,同时也避免了调用 setState 后,状态并未变化的带来的不必要的 Update 操作。

当父组件的 state 状态更新后,如果子组件的状态并未更新,那么子组件将不会再一层一层的去执行 Update 流程,从而达到优化性能的目的。
原载于:雨夜带刀’s Blog 链接:http://stylechen.com/react-and-immutable.html
从 React 的组件更新谈 Immutable 的应用的更多相关文章
- React和Vue的组件更新比较
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 24.0px "Helvetica Neue"; color: #404040 } p. ...
- 【React 6/100】 React原理 | setState | JSX语法转换 | 组件更新机制
****关键字 | setState | JSX语法转换 | 组件更新机制 组件更新机制 setState() 的两个作用 修改state 更新组件 过程:父组件重新渲染时,也会重新渲染子组件,但只会 ...
- React中组件间通信的方式
React中组件间通信的方式 React中组件间通信包括父子组件.兄弟组件.隔代组件.非嵌套组件之间通信. Props props适用于父子组件的通信,props以单向数据流的形式可以很好的完成父子组 ...
- 移动web端的react.js组件化方案
背景: 随着互联网世界的兴起,web前端开发的方式越来越多,出现了很多种场景开发的前端架构体系,也对前端的要求日益增高,早已经不是靠一个JQuery.js来做前端页面的时代了,而今移动端变化最大,近 ...
- [React] 多组件生命周期转换关系
前段时间一直在基于React做开发,最近得空做一些总结,防止以后踩坑. 言归正传,React生命周期是React组件运行的基础,本文主要是归纳多组件平行.嵌套时,生命周期转换关系. 生命周期 Reac ...
- React入门--------组件的生命周期
Mounting/组件挂载相关: componentWillMount componentDidMount Updating/组件更新相关: componentWillReceiveProps sho ...
- reactjs入门到实战(七)---- React的组件的生命周期
React的组件的生命周期有三个状态分别是:挂载(生产组件示例化.准备挂载到页面.挂载到页面).更新(更新值.更新DOM).和卸载(卸载后). >>>其他 getInitia ...
- React Native组件、生命周期及属性传值props详解
创建组件的三种方式 第一种:通过ES6的方式创建 /** * 方式一 :ES6 */ export default class HelloComponent extends Component { r ...
- React 面向组件化编程 - 封装了webpack - npm run build 产生的包的 /static 引用路径问题
React 面向组件化编程 面向对象 ----> 面向模块 ----> 面向组件 套路: 注意: 组件名必须大写开头: 只能有一个根标签: <input />虚拟DOM 元素必 ...
随机推荐
- c#-FrameWork02泛型
泛型 l 泛型(generic)编程是一种编程范式,它利用”参数化类型”将类型抽象化,从而可以实现更为灵活的复用.把数据类型参数化 sh泛型集合 泛型集合与集合的对比 泛型集合类 非泛型集合类 Li ...
- C# string Stream 互转
使用C#将字符串转化成流,将流转换成字符串,代码如下: using System.IO; using System.Text; namespace CSharpConvertString2Stream ...
- 2017年10月22日 基础SQL语句&数据库创建主外键关系
1.SQL语句的注释 双减号:-- 或者/**/2.创建数据库create database 数据库名称(不允许以数字开头,不允许以符号开头,不要起汉语名字) 3.如何选中这个数据库use 数据库名 ...
- 使用java applet通过签名访问客户端串口
前端时间公司有需求要访问客户端串口读取电子称的数据,通过网上资料,决定使用applet通过电子签名的形式实现. 1.先写applet:这里我是使用RXRTcomm.jar LocalFileApple ...
- Java循环结构之while和do-while循环
循环结构知识点 本章技能目标 1理解循环的含义 2会使用while循环 3会使用do-while循环 4会使用调试解决简单的程序错误 一. 循环的含义 循环就是重复的做……(Java程序中的循环结 ...
- VMware 安装提示缺少MicrosoftRuntime DLL 问题解决办法
VMware 安装提示缺少MicrosoftRuntime DLL 问题解决办法 刚刚安装VMware失败了试了好多办法,在这总结一下. 下面是程序的截图 这是报错信息 网上的解决方法: 当出现安装失 ...
- SQLAlchemy的使用---外键ForeignKey数据库创建与连接
# 一对多建表操作 from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() from sql ...
- PHP性能检测与优化—XHProf 数据阅读
PHP性能检测与优化—XHProf 数据阅读 一. 效果如下 请求总揽 函数调用情况 二. 参数含义 Inclusive Time 包括子函数所有执行时间 ...
- java:网络通讯
网络标准模型:开放式系统模型OSI https://www.cnblogs.com/lydit/articles/4499928.html 理解Scoket通讯:https://www.cnblogs ...
- SQL Server ->> DISABLE索引后插入更新数据再REBUILD索引 和 保留索引直接插入更新数据的性能差异
之前对于“DISABLE索引后插入更新数据再REBUILD索引 和 保留索引直接插入更新数据的性能差异”这两种方法一直认为其实应该差不多,因为无论如何索引最后都需要被维护,只不过是个时间顺序先后的问题 ...