batchedUpdates
1 )概述
- 在
requestWork
在中间, 会判断一个 isBatchingUpdates 做一些特定的操作 - batchedUpdates 是一个批量更新的操作, 什么是批量更新呢?先来看一个例子
2 )示例
index.js
import React from 'react'
import ReactDOM from 'react-dom'
import App from './demos/batchedUpdates'
ReactDOM.render(<App />, document.getElementById('root'))
demos/batchedUpdates
import React from 'react'
import { unstable_batchedUpdates as batchedUpdates } from 'react-dom'
export default class BatchedDemo extends React.Component {
state = {
number: 0,
}
handleClick = () => {
// 第一种场景 事件处理函数自带`batchedUpdates`
// this.countNumber()
// 第二种场景,主动`batchedUpdates`
// setTimeout(() => {
// this.countNumber()
// }, 0)
// 第三种场景,setTimeout中没有`batchedUpdates`
setTimeout(() => {
batchedUpdates(() => this.countNumber())
}, 0)
}
countNumber() {
const num = this.state.number
this.setState({
number: num + 1,
})
console.log(this.state.number)
this.setState({
number: num + 2,
})
console.log(this.state.number)
this.setState({
number: num + 3,
})
console.log(this.state.number)
}
render() {
return <button onClick={this.handleClick}>Num: {this.state.number}</button>
}
}
- 在上述
countNumber
中定义了更改 state的方法 - 当点击 按钮时,里面的 num 会更改,回调函数是
handleClick
- 第一种情况
- 当直接调用
countNumber
时,页面显示 3,但是 console 面板分别输出 3次0 - 注意 只获取一次 num, 初始化是 0,执行了3次setState, 最后一次是 0 + 3 = 3
- 这是因为 batchUpdtes 的概念所导致
- 当直接调用
- 第二种情况
- 在一个延迟器中调用
countNumber
- 这时候页面显示 3,console面板中分别输出 1, 2, 3
- 这是我们符合预期的输出
- 在一个延迟器中调用
- 第三种情况
- 在延迟器中使用 batchedUpdates 这个 API,来调用
countNumber
- 这时候,页面显示 3,console面板 依次输出 3次0
- 在延迟器中使用 batchedUpdates 这个 API,来调用
- 第一种情况
- 那么它(batchedUpdates)的原理是什么呢?
3 ) 源码
进入到 requestWork 函数中的相关判断,可以在源码 nodemodules/react-dom/cjs/react-dom-development.js 中
找到 requestWork 进行debugger,重启 react项目,之后在 Chrome 中调试
if (isBatchingUpdates) {
// Flush work at the end of the batch.
if (isUnbatchingUpdates) {
// ...unless we're inside unbatchedUpdates, in which case we should
// flush it now.
nextFlushedRoot = root;
nextFlushedExpirationTime = Sync;
performWorkOnRoot(root, Sync, true);
}
return;
}
-
第一种场景下
-
isBatchingUpdates
是 true,isUnbatchingUpdates
是 false - 这样,最终直接 return 了, 就没有进入后续的调度,就什么都没做
- 通过调用堆栈可知,3个 update 调用完成后,存入 updateQueue之后
- 会调用
performSyncWork
来帮助我们完成任务调度的过程,而非通过requestWork
来完成的任务调度 - 这样,任务更新队列就在一起把state更新了, 所以要等到 state 所有都变掉之后,才会执行state的更新
- 所以 console 面板中打印的,不是真正更新后的 state
- 这就是 batchUpdates 让我们在一个方法调用里面,所有的setState都是一次性更新到我们的state里面的
- 而不是每次都调用 setState 进入调度和渲染,以此来提升性能效率
-
-
第二种场景下
- 当 setTimeout 执行后,方法执行上下文已不是 handleClick这个函数了,而是 window 了
- 这时的 batchUpdates 已重置了
- 这时候调用
countNumber
就没有isBatchingUpdates
为 true 的情况了,就不会批量执行 - 所以,每次 setState 都会更新,导致我们每次打印的都是新的 state
- 这种会导致,我们的应用性能变得很低,所以这种是极不推荐的
-
第三种场景下
- 基于
batchedUpdates
的 API 调用countNumber
- 这个 API 的作用是 设置上下文,handleClick 是react里面的事件体系产生的回调
- 事件体系会主动调用
batchedUpdates
或interactiveUpdates
帮助我们设置上下文 - 所以,在react事件中大部分回调都是会帮助我们做批量更新的
- 而不会让我们在事件的回调中做多次的更新
- 这是react 事件绑定中的性能优化
- 基于
在 ReactFiberSchduler.js 中的 batchedUpdates
中文章来源:https://www.toymoban.com/news/detail-799380.html
// TODO: Batching should be implemented at the renderer level, not inside
// the reconciler.
function batchedUpdates<A, R>(fn: (a: A) => R, a: A): R {
const previousIsBatchingUpdates = isBatchingUpdates;
isBatchingUpdates = true;
try {
return fn(a);
} finally {
isBatchingUpdates = previousIsBatchingUpdates;
if (!isBatchingUpdates && !isRendering) {
performSyncWork();
}
}
}
- 它设置
isBatchingUpdates = true;
,在设置之前记录前值const previousIsBatchingUpdates = isBatchingUpdates;
- 当我们的回调 fn 被调用之后,在 finally 中 恢复
isBatchingUpdates
成之前的值previousIsBatchingUpdates
- 之后,判断是否符合
!isBatchingUpdates && !isRendering
符合了,则调用performSyncWork
方法
同样,unbatchedUpdates
也是来自这个文件文章来源地址https://www.toymoban.com/news/detail-799380.html
// TODO: Batching should be implemented at the renderer level, not inside
// the reconciler.
function unbatchedUpdates<A, R>(fn: (a: A) => R, a: A): R {
if (isBatchingUpdates && !isUnbatchingUpdates) {
isUnbatchingUpdates = true;
try {
return fn(a);
} finally {
isUnbatchingUpdates = false;
}
}
return fn(a);
}
- 它设置
isUnbatchingUpdates
为 true - 它也是上下文的关系,让我们在调用 fn 的时候有这么一个值是 true 的上下文
- 标记产生的不一样的变化
- 以上是批量更新的概念和原理
- 扩展
- 从上面我们知道
setState
这个方法是同步的, - 但是调用之后,并不标志 react 的状态立马更新
- 这个更新是需要根据当前环境的上下文来判断的
- 如果处于批量更新的情况下,state不是立马更新的
- 如果不处于批量更新的情况下,state 可能会立马更新
- 因为 有 ConcurrentMode 这种异步渲染的情况
- 这种情况下,state 也不是立马就更新的
- 进入异步更新,就会进入到异步调度的过程中
- 从上面我们知道
到了这里,关于React16源码: React中调度之batchedUpdates的源码实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!