React合成事件

为什么需要合成事件

  1. 兼容浏览器监听写法
  2. 避免大量节点绑定事件占用内存,将事件委托到document上,有统一的事件处理函数,等事件冒泡到document上时,reacttarget节点往上遍历父元素,判断有没有元素绑定对应事件,有则触发该事件。
  3. 事件回调函数处理完成后需要清空state队列中的更新,重新渲染视图。

事件持久化

event.persist,每当事件回调函数被触发后,合成事件对象都会被清空,保证其垃圾回收。如果需要异步处理事件,则需要执行event.persist,需要的事件对象会被保留,清空操作的对象则是一个新创建的对象。SyntheticEvent就是合成事件对象,这个对象会被重复使用,避免对象的重复创建开销。

setState

当执行setState时,先将当前要更新的state存储在Updater组件实例上,然后判断当前是否处于批量更新状态,如果是,则将当前Updater实例存储下来,等到非批量更新的时候一起更新。

在处理事件的时候,因为可能在回调函数中的动作会引起多个组件的更新,为了性能优化,react会进入批量更新状态。在代码中就是在统一的事件处理函数中将状态设置为批量更新状态。等当前事件执行结束后(遍历完所有的父节点,执行完所有的回调函数)将状态设置为false。并且执行batchUpdate批量执行更新逻辑。

setState流程
state={count: 0}
handleClick = () => {
  this.setState({
      count:   this.state.count + 1
  })
 // 0
  console.log(this.state.count)
  this.setState((prevState) => ({
    count: prevState.count + 1
  }))
  //  0
  console.log(this.state.count)

  setTimeout(() => {
   this.setState((prevState) => ({
      count: prevState.count + 1
    }))
    // 3
    console.log(this.state.count)

   this.setState((prevState) => ({
      count: prevState.count + 1
    }))
    // 4
    console.log(this.state.count)
  }, 0)
}

执行更新逻辑时,如果render函数返回的是函数组件,前后对比两次函数执行结果,进行dom-diff,执行需要的节点更新逻辑。如果返回的都是类组件,并不是直接调用render方法,将前后结果进行比对,而是调用返回组件实例上的updater. emitUpdate方法,这样的话,就会执行子组件中的componentShouldUpdate方法判断此次是否需要更新,如果需要则执行forceUpdate方法再次执行dom-diff逻辑判断,这个时候子组件返回的就是普通的原生节点,按原有逻辑进行比对即可。

在构造函数被new之后,componentWillMount被调用,调用实例的render函数,会递归解析返回的元素,解析子组件,实例化子组件,子组件componentWillMount被调用,调用子组件render函数,componentDidMount完成所有的子元素解析后,执行父组件的componentDidMount后完毕。当父组件调用setState,生命周期执行顺序为

  1. 父组件shouldComponentUpdate
  2. 父组件componentWillUpdate
  3. 子组件componentWillReceiveProps
  4. 子组件shouldComponentUpdate
  5. 子组件componentWillUpdate
  6. 子组件componentDidUpdate
  7. 父组件componentDidUpdate

为什么不能将index作为key

因为react在dom-diff对比时,会将老节点列表中的key相同的元素保留作为新节点列表中对应key的元素。

dom-diff规则

  1. 对树来说,如果出现组件移动的情况,不处理,直接销毁组件重新生成
  2. 如果组件/节点类型发生变化则直接替换
  3. 同一层级的dom变更:增加、删除、移动(删除+插入 )

dom-diff算法

  1. 将老节点信息存储在一个对象中,键名为key,键值为虚拟dom对象
  2. 遍历新子元素数组,先去对象中查找有没有相同key的虚拟dom,如果有,则复用该虚拟dom并更新节点属性,再判断一下虚拟dom在原节点层次中的位置,如果比lastIndex小,则移动(删除当前节点,将虚拟dom拷贝到新位置),否则就不动(lastIndex会在遍历时变大)。相当于节点会不断往右移动。lastIndex用于判断可复用的节点是否需要移动。

你可能感兴趣的:(React合成事件)