从 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 元素必 ...
随机推荐
- 安卓API版本
- vue 上拉加载更多
var _this=this; var goods_id = _this.$route.query.id; var isscroll = true; _this.$nextTick(() => ...
- iDempiere 使用指南 系统安装 以及 virtualbox虚拟机下载
Created by 蓝色布鲁斯,QQ32876341,blog http://www.cnblogs.com/zzyan/ iDempiere官方中文wiki主页 http://wiki.idemp ...
- c++类模板成员函数报错
类模板成员函数要不就在类模板中实现,要不就和类模板写在同一个文件中. 否则然会出现下面错误: >main.obj : error LNK2019: 无法解析的外部符号 "public: ...
- 好记性不如烂笔头-nginx安装环境与Linux安装ftp组件
Nginx安装环境 1. Nginx安装环境 Nginx是C语言开发,建议在linux上运行,我参加工作这些年来一直使用Linux发行版之一的 Centos作为安装环境. 1.1 gcc 安装Ngin ...
- 多线程 更新 winform 控件的值,以避免UI线程的卡顿
委托 private delegate void UpdateDGV_AddRes_CallBack(Int32 i,bool Res); 函数实现 private void UpdateDGV_De ...
- Hadoop ->> HDFS(Hadoop Distributed File System)
HDFS全称是Hadoop Distributed File System.作为分布式文件系统,具有高容错性的特点.它放宽了POSIX对于操作系统接口的要求,可以直接以流(Stream)的形式访问文件 ...
- React学习笔记 - 元素渲染
React Learn Note 3 React学习笔记(三) 标签(空格分隔): React JavaScript 二.元素渲染 元素是构成react应用的最小单位. 元素是普通的对象. 元素是构成 ...
- 【java开发系列】—— Tomcat编译报错
由于之前Eclipse里面有一个可移植性的web工程,但是在我很久没用后,再次登录这个IDE的时候就发现了问题. 首先,我的电脑里面有两个版本的JDK,1.6和1.7.两个版本的Tomcat6和7以及 ...
- June 05th 2017 Week 23rd Monday
No great discovery was ever made without a bold guess. 没有大胆的猜测就没有伟大的发现. I've read this sentence just ...