native react 更新机制_从源码全面剖析 React 组件更新机制

React 把组件看作状态机(有限状态机), 使用state来控制本地状态, 使用props来传递状态. 前面我们探讨了 React 如何映射状态到 UI 上(初始渲染), 那么接下来我们谈谈 React 时如何同步状态到 UI 上的, 也就是:

React 是如何更新组件的?

React 是如何对比出页面变化最小的部分?

这篇文章会为你解答这些问题.

在这之前

你已经了解了React (15-stable版本)内部的一些基本概念, 包括不同类型的组件实例、mount过程、事务、批量更新的大致过程(还没有? 不用担心, 为你准备好了从源码看组件初始渲染、接着从源码看组件初始渲染);

准备一个demo, 调试源码, 以便更好理解;

Keep calm and make a big deal !

React 是如何更新组件的?

TL;DR

依靠事务进行批量更新;

一次batch(批量)的生命周期就是从ReactDefaultBatchingStrategy事务perform之前(调用ReactUpdates.batchUpdates)到这个事务的最后一个close方法调用后结束;

事务启动后, 遇到 setState 则将 partial state 存到组件实例的_pendingStateQueue上, 然后将这个组件存到dirtyComponents 数组中, 等到 ReactDefaultBatchingStrategy事务结束时调用runBatchedUpdates批量更新所有组件;

组件的更新是递归的, 三种不同类型的组件都有自己的updateComponent方法来决定自己的组件如何更新, 其中 ReactDOMComponent 会采用diff算法对比子元素中最小的变化, 再批量处理.

这个更新过程像是一套流程, 无论你通过setState(或者replaceState)还是新的props去更新一个组件, 都会起作用.

那么具体是什么?

让我们从这套更新流程的开始部分讲起...

调用 setState 之前

首先, 开始一次batch的入口是在ReactDefaultBatchingStrategy里, 调用里面的batchedUpdates便可以开启一次batch:

// 批处理策略

var ReactDefaultBatchingStrategy = {

isBatchingUpdates: false,

batchedUpdates: function(callback, a, b, c, d, e) {

var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;

ReactDefaultBatchingStrategy.isBatchingUpdates = true; // 开启一次batch

if (alreadyBatchingUpdates) {

return callback(a, b, c, d, e);

} else {

// 启动事务, 将callback放进事务里执行

return transaction.perform(callback, null, a, b, c, d, e);

}

},

};

在 React 中, 调用batchedUpdates有很多地方, 与更新流程相关的如下

// ReactMount.js

ReactUpdates.batchedUpdates(

batchedMountComponentIntoNode, // 负责初始渲染

componentInstance,

container,

shouldReuseMarkup,

context,

);

// ReactEventListener.js

dispatchEvent: function(topLevelType, nativeEvent) {

...

try {

ReactUpdates.batchedUpdates(handleTopLevelImpl, bookKeeping); // 处理事件

} finally {

TopLevelCallbackBookKeeping.release(bookKeeping);

}

},

第一种情况, React 在首次渲染组件的时候会调用batchedUpdates, 然后开始渲染组件. 那么为什么要在这个时候启动一次batch呢? 不是因为要批量插入, 因为插入过程是递归的, 而是因为组件在渲染的过程中, 会依顺序调用各种生命周期函数, 开发者很可能在生命周期函数中(如componentWillMount或者componentDidMount)调用setState. 因此, 开启一次batch就是要存储更新(放入dirtyComponents), 然后在事务结束时批量更新. 这样以来, 在初始渲染流程中, 任何setState都会生效, 用户看到的始终是最新的状态.

第二种情况, 如果你在HTML元素上或者组件上绑定了事件, 那么你有可能在事件的监听函数中调用setState, 因此, 同样为了存储更新(放入dirtyComponents), 需要启动批量更新策略. 在回调函数被调用之前, React事件系统中的dispatchEvent函数负责事件的分发, 在dispatchEvent中启动了事务, 开启了一次batch, 随后调用了回调函数. 这样一来, 在事件的监听函数中调用的setState就会生效.

也就是说, 任何可能调用 setState 的地方, 在调用之前, React 都会启动批量更新策略以提前应对可能的setState

那么调用 batchedUpdates 后发生了什么?

React 调用batchedUpdates时会传进去一个函数, batchedUpdates会启动ReactDefaultBatchingStrategyTransaction事务, 这个函数就会被放在事务里执行:

// ReactDefaultBatchingStrategy.js

var transaction = new ReactDefaultBatchingStrategyTransaction(); // 实例化事务

var ReactDefaultBatchingStrategy = {

...

batchedUpdates: function(callback, a, b, c, d, e) {

...

return transaction.perform(callback, null, a, b, c, d, e); // 将callback放进事务里执行

...

};

ReactDefaultBatchingStrategyTransaction这个事务控制了批量策略的生命周期:

// ReactDefaultBatchingStrategy.js

var FLUSH_BATCHED_UPDATES = {

initial

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值