从 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 的生命周期中的一系列方法:
componentWillReceiveProps
shouldComponentUpdate
componentWillUpdate
componentDidUpdate
当然,还会执行 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 元素必 ...
随机推荐
- linux下为.net core应用创建守护进程
1.Supervisor 安装 yum install python-setuptools easy_install supervisor 2.配置 Supervisor mkdir /etc/sup ...
- SQL脚本整理系列一 表分区
表分区的目的: 1.把历史数据放到另外一个表里面 可以提高查询效率 当然如果经常查询历史数据和新数据的合并结果集这样做就大大的不好了 2.通过把一个表放到不同的文件,不同的文件再存储到不同的磁盘列阵中 ...
- 9、搜索 :ion-searchbar
/* ---html----*/ <ion-searchbar [(ngModel)]="searchQuery" (input)="getItems($event ...
- 一、IP地址
IP地址 1)网络地址 IP地址由网络号(包括子网号)和主机号组成,网络地址的主机号为全0,网络地址代表着整个网络. 2)广播地址 广播地址通常称为直接广播地址,是为了区分受限广播地址. 广播地址与网 ...
- HttpServletRequest的获取客户端真实IP
摘自:http://chenyoulu.diandian.com/post/2012-11-14/40042540378 request方法客户端IP: request.getRemoteAddr() ...
- js中的Function和Object
说到构造器(condtructor).原型链(prototype),说道Function与Object,总要祭出下面这张图 1.Function是最顶层的构造器,Object是最顶层的对象 2.先有的 ...
- python发送邮件(带附件)
python通过stmp发送qq邮件,带附件 import smtplib from email.mime.multipart import MIMEMultipart from email.mime ...
- python的用户输入和while循环
1.函数input()工作原理 函数input()让程序暂停运行,等待用户输入一些文本.获取用户输入后,Python将其存储在一个变量中,以方便你使用. (1)获取数值可以用 int()函数 (2)求 ...
- css浮动布局小技巧
父元素如何围住浮动的子元素的三种办法: 一.为父元素应用overflow:hidden. overflow真正用途是防止包含元素被大的内容撑开,设定了宽度之后,包含元素将超过容器的内容减掉:而它还有另 ...
- OPENCV VS设置
OPENCV VS设置 第一步 工程->工具->选项->VC++目录 第二步 这两项放到系统path下 D:\OpenCV2.4.3\VS\bin\Debug;D:\OpenCV2. ...