在介绍 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 的应用的更多相关文章

  1. React和Vue的组件更新比较

    p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 24.0px "Helvetica Neue"; color: #404040 } p. ...

  2. 【React 6/100】 React原理 | setState | JSX语法转换 | 组件更新机制

    ****关键字 | setState | JSX语法转换 | 组件更新机制 组件更新机制 setState() 的两个作用 修改state 更新组件 过程:父组件重新渲染时,也会重新渲染子组件,但只会 ...

  3. React中组件间通信的方式

    React中组件间通信的方式 React中组件间通信包括父子组件.兄弟组件.隔代组件.非嵌套组件之间通信. Props props适用于父子组件的通信,props以单向数据流的形式可以很好的完成父子组 ...

  4. 移动web端的react.js组件化方案

     背景: 随着互联网世界的兴起,web前端开发的方式越来越多,出现了很多种场景开发的前端架构体系,也对前端的要求日益增高,早已经不是靠一个JQuery.js来做前端页面的时代了,而今移动端变化最大,近 ...

  5. [React] 多组件生命周期转换关系

    前段时间一直在基于React做开发,最近得空做一些总结,防止以后踩坑. 言归正传,React生命周期是React组件运行的基础,本文主要是归纳多组件平行.嵌套时,生命周期转换关系. 生命周期 Reac ...

  6. React入门--------组件的生命周期

    Mounting/组件挂载相关: componentWillMount componentDidMount Updating/组件更新相关: componentWillReceiveProps sho ...

  7. reactjs入门到实战(七)---- React的组件的生命周期

    React的组件的生命周期有三个状态分别是:挂载(生产组件示例化.准备挂载到页面.挂载到页面).更新(更新值.更新DOM).和卸载(卸载后). >>>其他     getInitia ...

  8. React Native组件、生命周期及属性传值props详解

    创建组件的三种方式 第一种:通过ES6的方式创建 /** * 方式一 :ES6 */ export default class HelloComponent extends Component { r ...

  9. React 面向组件化编程 - 封装了webpack - npm run build 产生的包的 /static 引用路径问题

    React 面向组件化编程 面向对象 ----> 面向模块 ----> 面向组件 套路: 注意: 组件名必须大写开头: 只能有一个根标签: <input />虚拟DOM 元素必 ...

随机推荐

  1. MAC 下安装RabbitMQ

    1.使用brew来安装 RabbitMQ(地址:http://www.rabbitmq.com/install-standalone-mac.html ) 2.安装目录 /usr/local/Cell ...

  2. UWP 应用获取各类系统、用户信息 (1) - 设备和系统的基本信息、应用包信息、用户数据账户信息和用户账户信息

    应用开发中,开发者时常需要获取一些系统.用户信息用于数据统计遥测.问题反馈.用户识别等功能.本文旨在介绍在 Windows UWP 应用中获取一些常用系统.用户信息的方法.示例项目代码可参见 Gith ...

  3. 多线程TCP的socket通信

    应用多线程来实现服务器与多客户端之间的通信. 基本步骤: 1.服务器端创建ServerSocket,循环调用accept()等待客户端的连接 2.客户端创建一个socket并请求和服务器端的连接 3. ...

  4. Java环境搭建与配置、以及Tomcat搭建与配置

    首先配置Java jdk环境  列如:jdk1.8.0 : 1.首先windows+e打开文件管理系统,找到系统属性,点击“高级系统设置” ,再点击环境变量: 2.在系统变量里边新建:JAVA_HOM ...

  5. netty笔记(一)--Demo

    Netty是一个Java开源框架,用于传输数据.由server和client组成,封装了Java nio,支持TCP, UDP等协议.这里写了一Demo EchoClientHandler.java ...

  6. Zepto结合Swiper的选项卡

    我们昨天说了关于Angular的选项卡,那今天就说一下Swiper的选项卡吧! 今天的选项卡是Zepto结合Swiper的选项卡,咱么明天再说纯纯的Swiper的吧! 既然是关于Zepto和Swipe ...

  7. Grunt压缩图片和JS

    今天我们来说一下用Grunt来压缩图片和JS吧! 首先要安装插件: 这是压缩图片的; npm install --save-dev gulp-imagemin 这是压缩JS的: npm install ...

  8. .NET开源工作流RoadFlow-表单设计-数据表格

    数据表格即在表单中显示一个table,该table数据可以来自任意自定义的来源: 数据类型:指定表格的数据源类型 1.datatable,即.net中的System.Data.DataTable 2. ...

  9. 转:ArcGIS提取面状道路中心线(转载)

    1.首先把所有的面要素merge成一个要素 2.把merge后的数据转成线数据 3.此时转换后的线数据一定是闭合的,为了防止提取中心线失败(只提取出外围轮廓)我们在随意一个道路末端使用打断工具打一个开 ...

  10. Intent的简单使用

    主要实现Intent之间值得转递,如从AActivity到BActivity之间传一个数值,一个实体类,一个集合类 下面代码只要是实现对startActivityForResult的使用,用ABC 3 ...