在上一节的基础上修改代码。

前置知识:

react基础篇第五点:https://www.yuque.com/linhe-8mnf5/fxyxkm/tnkpyg#jBiGv
原理篇:https://www.yuque.com/linhe-8mnf5/fxyxkm/daa695

  • 在react中事件是异步的,事件更新是批量的。
  • 调用setState并没有立马更新,而是先缓存起来,等事件函数完成后在进行批量更新,一次更新并重新渲染。


1、改造index.js,先手动实现批量更新

  1. // index.js
  2. handleClick = () => {
  3. updateQueue.isBatchingUpdate = true
  4. this.setState({
  5. number: this.state.number + 1
  6. })
  7. console.log(this.state.number)
  8. setTimeout(() => {
  9. this.setState({
  10. number: this.state.number + 1
  11. })
  12. console.log(this.state.number)
  13. }, 0)
  14. updateQueue.batchUpdate()
  15. }

2、改造Component.js

  1. // Component.js
  2. import { createDom } from './react-dom'
  3. // 更新队列
  4. export const updateQueue = {
  5. isBatchingUpdate: false, // 当前是否处于批量更新模式,默认值false
  6. updaters: new Set(), // 传入的setState集合
  7. // 批量更新方法
  8. batchUpdate() {
  9. for (let updater of this.updaters) {
  10. updater.updateClassComponent()
  11. }
  12. this.isBatchingUpdate = false
  13. }
  14. }
  15. class Updater {
  16. constructor(classInstance) {
  17. this.classInstance = classInstance // 类组件实例
  18. this.penddingStates = [] // 等待生效的状态
  19. this.callbacks = []
  20. }
  21. addState(partialState, callback) {
  22. this.penddingStates.push(partialState) // 等待生效的状态
  23. if (typeof callback === 'function') {
  24. this.callbacks.push(callback) // 更新后的回调
  25. }
  26. // 如果当前是批量更新模式,先缓存
  27. if (updateQueue.isBatchingUpdate) {
  28. updateQueue.updaters.add(this)
  29. } else {
  30. // 直接更新组件
  31. this.updateClassComponent()
  32. }
  33. }
  34. updateClassComponent() {
  35. const { penddingStates, classInstance, callbacks } = this
  36. // 说明有setState
  37. if (penddingStates.length > 0) {
  38. classInstance.state = this.getState() // 计算新状态
  39. // 更新dom
  40. classInstance.forceUpdate()
  41. // 调用setState回调
  42. callbacks.forEach(callback => callback())
  43. // 清空callback
  44. callbacks.length = 0
  45. }
  46. }
  47. getState() {
  48. let { penddingStates, classInstance } = this
  49. let { state } = classInstance
  50. // 老状态新状态合并
  51. penddingStates.forEach(nextState => {
  52. // setState传值为函数,要传入老状态,返回新状态,在进行合并
  53. if (typeof nextState === 'function') {
  54. nextState = nextState(false)
  55. }
  56. // 合并状态,新状态覆盖老状态
  57. state = { ...state, ...nextState }
  58. })
  59. // 清空等待生效的状态
  60. penddingStates.length = 0
  61. return state
  62. }
  63. }
  64. // 被继承的父类Component
  65. class Component {
  66. static isReactComponent = true
  67. constructor(props) {
  68. this.props = props
  69. this.state = {}
  70. this.Updater = new Updater(this)
  71. }
  72. // 被调用的this.setState()方法
  73. setState(partialState, callback) {
  74. this.Updater.addState(partialState, callback)
  75. }
  76. forceUpdate() {
  77. let newVdom = this.render() //获取新dom
  78. updateClassComponent(this, newVdom) // 更新
  79. }
  80. }
  81. // 更新类组件
  82. function updateClassComponent(classInstance, newVdom) {
  83. const oldDom = classInstance.dom // 取出类组件上次渲染出来的真实dom
  84. // 创建新的DOM
  85. const newDom = createDom(newVdom)
  86. // 新的替换老的
  87. oldDom.parentNode.replaceChild(newDom, oldDom)
  88. classInstance.dom = newDom
  89. }
  90. export default Component

3、实现效果

image.png

4、源代码

本文地址:https://gitee.com/linhexs/react-write/tree/4.update-queue/