react 17 合成事件

907 阅读2分钟

事件流程

  • 事件捕获
  • 事件目标
  • 事件冒泡
  • 事件委托
  • 先绑定先执行

事件系统变更

  • 更改事件委托

    • 首先第一个修改点就是更改了事件委托绑定节点,在16版本中,React都会把事件绑定到页面的document元素上,这在多个React版本共存的情况下就会虽然某个节点上的函数调用了event.stopPropagation(),但还是会导致另外一个React版本上绑定的事件没有被阻止触发,所以在17版本中会把事件绑定到render函数的节点上
  • 去除事件池

    • 17版本中移除了事件对象池,这是因为 React 在旧浏览器中重用了不同事件的事件对象,以提高性能,并将所有事件字段在它们之前设置为 null。在 React 16 及更早版本中,使用者必须调用event.persist() 才能正确的使用该事件,或者正确读取需要的属性

image.png

//合成事件的统一代理处理函数
function dispatchEvent(event) {
  let { target, type } = event;//target=button type =click
  let eventType = `on${type}`;//onclick
  let syntheticEvent = createSyntheticEvent(event);
  updateQueue.isBatchingUpdate = true;//事件函数执行前先设置批量更新模式为true
  //在此我们要模拟React事件的冒泡
  while (target) {
    let { store } = target;
    let handler = store && store[eventType]
    handler && handler(syntheticEvent);
    //在执行handler的过程中有可能会阻止冒泡
    if (syntheticEvent.isPropagationStopped) {
      break;
    }
    target = target.parentNode;
  }
  updateQueue.isBatchingUpdate = false;
  updateQueue.batchUpdate();
}
/**
 * 为什么React不会原生事件对象直接传给事件处理函数
 * 1.为了兼容性 像jquery一样,抹平浏览器的差异
 * @param {*} nativeEvent 
 * @returns 
 */
function createSyntheticEvent(nativeEvent) {
  let syntheticEvent = {};
  for (let key in nativeEvent) {//把原生事件上的属性拷贝到合成事件对象上去
    syntheticEvent[key] = nativeEvent[key];
  }
  syntheticEvent.nativeEvent = nativeEvent;
  syntheticEvent.isDefaultPrevented = false;
  syntheticEvent.isPropagationStopped = false;
  syntheticEvent.preventDefault = preventDefault;
  syntheticEvent.stopPropagation = stopPropagation;
  return syntheticEvent;
}
function preventDefault() {
  this.defaultPrevented = true;
  const event = this.nativeEvent;
  if (event.preventDefault) {
    event.preventDefault();
  } else {//IE
    event.returnValue = false;
  }
  this.isDefaultPrevented = true;
}
function stopPropagation() {
  const event = this.nativeEvent;
  if (event.stopPropagation) {
    event.stopPropagation();
  } else {//IE
    event.cancelBubble = true;
  }
  this.isPropagationStopped = true;
}