React16源码: React中event事件触发的源码实现

React核心源码解读 - 学习笔记 《 1. 配置 React 源码本地调试环境 使用 create-react-app 脚手架创建项目 npx create-react-app react-test 弹射 create-react-app 脚手架内部配置 npm run eject 克隆 react 官方源码 (在项目的根目录下进行克隆) git clone --branch v16.13.1 --depth=1 https://github.com/facebook/react.git src/react 链接本地源码 / 阅读详情

event 事件触发过程


1 )概述

  • 在之前事件绑定时,绑定的是两个方法
    • 一个是 dispatchInteractiveEvent
    • 另外一个 dispatchEvent
  • 其实它们调用的方法都是差不多的,一开始会有一点小的区别

2 )源码

定位到 packages/react-dom/src/events/ReactDOMEventListener.js#L165

进入 trapCapturedEvent

export function trapCapturedEvent(
  topLevelType: DOMTopLevelEventType,
  element: Document | Element,
) {
  if (!element) {
    return null;
  }
  // 注意这里,根据是否是 Interactive 类型的事件,调用的不同的回调,最终赋值给 dispatch
  const dispatch = isInteractiveTopLevelEventType(topLevelType)
    ? dispatchInteractiveEvent
    : dispatchEvent;

  addEventCaptureListener(
    element,
    getRawEventName(topLevelType),
    // Check if interactive and wrap in interactiveUpdates
    // 这边的topleveltype呢,是我们在进行 dom 的事件绑定的时候已经通过 bind 给它绑定好了
    // 在绑定事件的时候,就已经确定了一个值, 比如说是onChange这类的toplevel的事件名称
    dispatch.bind(null, topLevelType),
  );
}

2.1 先看 dispatchInteractiveEvent

定位到 packages/react-dom/src/events/ReactDOMEventListener.js#L184

// packages/react-dom/src/events/ReactDOMEventListener.js#L184
// nativeEvent,就是我们事件触发的时候,我们的domm的事件体系会给我们一个event对象
// 可以通过它来进入默认行为之类的这么一个事件对象
function dispatchInteractiveEvent(topLevelType, nativeEvent) {
  interactiveUpdates(dispatchEvent, topLevelType, nativeEvent);
}

// packages/events/ReactGenericBatching.js#L55
export function interactiveUpdates(fn, a, b) {
  return _interactiveUpdatesImpl(fn, a, b);
}

// packages/events/ReactGenericBatching.js#L23
let _interactiveUpdatesImpl = function(fn, a, b) {
  return fn(a, b);
};
  • 可见 dispatchInteractiveEvent 最终还是要调用 dispatchEvent, 只是多包了一层

2.2 进入 dispatchEvent

定位到 packages/react-dom/src/events/ReactDOMEventListener.js#L188

export function dispatchEvent(
  topLevelType: DOMTopLevelEventType,
  nativeEvent: AnyNativeEvent,
) {
  if (!_enabled) {
    return;
  }

  // 注意这里
  const nativeEventTarget = getEventTarget(nativeEvent);
  let targetInst = getClosestInstanceFromNode(nativeEventTarget);
  // 存在 符合条件的,并且没有被挂载
  if (
    targetInst !== null &&
    typeof targetInst.tag === 'number' &&
    !isFiberMounted(targetInst)
  ) {
    // If we get an event (ex: img onload) before committing that
    // component's mount, ignore it for now (that is, treat it as if it was an
    // event on a non-React tree). We might also consider queueing events and
    // dispatching them after the mount.
    targetInst = null; // 这个 target 置空
  }

  // 这里只是一个对象,用于携带信息
  const bookKeeping = getTopLevelCallbackBookKeeping(
    topLevelType,
    nativeEvent,
    targetInst,
  );

  try {
    // Event queue being processed in the same cycle allows
    // `preventDefault`.
    batchedUpdates(handleTopLevel, bookKeeping);
  } finally {
    releaseTopLevelCallbackBookKeeping(bookKeeping);
  }
}
  • 首先它要获取 nativeEventTarget,这个 target 就是我们 event 对象上面的 target

  • 它是出于对各种系统的一个兼容调用的一个方法来进行一个 Polyfill

  • 进入这个 getEventTarget 方法

    import {TEXT_NODE} from '../shared/HTMLNodeType';
    
    /**
     * Gets the target node from a native browser event by accounting for
     * inconsistencies in browser DOM APIs.
     *
     * @param {object} nativeEvent Native browser event.
     * @return {DOMEventTarget} Target node.
     */
    function getEventTarget(nativeEvent) {
      // 这个是对 IE9 的兼容
      // Fallback to nativeEvent.srcElement for IE9
      // https://github.com/facebook/react/issues/12506
      let target = nativeEvent.target || nativeEvent.srcElement || window;
    
      // Normalize SVG <use> element events #4963
      if (target.correspondingUseElement) {
        target = target.correspondingUseElement;
      }
    
      // Safari may fire events on text nodes (Node.TEXT_NODE is 3).
      // @see http://www.quirksmode.org/js/events_properties.html
      return target.nodeType === TEXT_NODE ? target.parentNode : target;
    }
    
    • 这个方法主要是为了兼容浏览器API获取 target 对象, 最终根据是否是 TEXT_NODE 返回 target.parentNode 或 target
  • 进入 getClosestInstanceFromNode

    export function getClosestInstanceFromNode(node) {
      // 存在,直接 return
      if (node[internalInstanceKey]) {
        return node[internalInstanceKey]; // 这个就是 初始化dom节点的时候,在node上插入这么一个key, 来指定对应的 fiber 对象
      }
    
      // 不存在,一直找 parentNode
      while (!node[internalInstanceKey]) {
        if (node.parentNode) {
          node = node.parentNode;
        } else {
          // Top of the tree. This node must not be part of a React tree (or is
          // unmounted, potentially).
          return null;
        }
      }
      // 这个 inst 就是一个 fiber 对象, 找到是 HostComponent 或 HostText 类型的
      let inst = node[internalInstanceKey];
      if (inst.tag === HostComponent || inst.tag === HostText) {
        // In Fiber, this will always be the deepest root.
        return inst;
      }
    
      return null;
    }
    
  • 进入 isFiberMounted

    const MOUNTING = 1;
    const MOUNTED = 2;
    const UNMOUNTED = 3;
    
    function isFiberMountedImpl(fiber: Fiber): number {
      let node = fiber;
      // 不存在,说明是即将插入或没有插入的节点
      if (!fiber.alternate) {
        // If there is no alternate, this might be a new tree that isn't inserted
        // yet. If it is, then it will have a pending insertion effect on it.
        // 这种是即将要插入的
        if ((node.effectTag & Placement) !== NoEffect) {
          return MOUNTING;
        }
        // 如果当前不是,向上找父节点,也是即将插入的
        while (node.return) {
          node = node.return;
          if ((node.effectTag & Placement) !== NoEffect) {
            return MOUNTING;
          }
        }
      } else {
        // 存在 alternate, 继续向上找
        while (node.return) {
          node = node.return;
        }
      }
      // 如果上级存在,并且是 HostRoot 说明已经被挂载了
      if (node.tag === HostRoot) {
        // TODO: Check if this was a nested HostRoot when used with
        // renderContainerIntoSubtree.
        return MOUNTED;
      }
      // If we didn't hit the root, that means that we're in an disconnected tree
      // that has been unmounted.
      // 其他情况都是未挂载的
      return UNMOUNTED;
    }
    
    export function isFiberMounted(fiber: Fiber): boolean {
      return isFiberMountedImpl(fiber) === MOUNTED;
    }
    
  • 进入 getTopLevelCallbackBookKeeping

    // packages/react-dom/src/events/ReactDOMEventListener.js#L50
    const CALLBACK_BOOKKEEPING_POOL_SIZE = 10;
    const callbackBookkeepingPool = [];
    
    // Used to store ancestor hierarchy in top level callback
    function getTopLevelCallbackBookKeeping(
      topLevelType,
      nativeEvent,
      targetInst,
    ): {
      topLevelType: ?DOMTopLevelEventType,
      nativeEvent: ?AnyNativeEvent,
      targetInst: Fiber | null,
      ancestors: Array<Fiber>,
    } {
      if (callbackBookkeepingPool.length) {
        const instance = callbackBookkeepingPool.pop();
        instance.topLevelType = topLevelType;
        instance.nativeEvent = nativeEvent;
        instance.targetInst = targetInst;
        return instance;
      }
      return {
        topLevelType,
        nativeEvent,
        targetInst,
        ancestors: [],
      };
    }
    
    • 这个函数就是一缓存机制
    • 最终我们在执行完整个react事件之后,会把这个对象再归还到这个pool里面来进行一个存储
    • 让浏览器的js运行环境不去删除这个对象,也让我们在后期不需要去重新创建一个对象,
    • 以减少这个对象声明和对象垃圾回收的一个性能开销
  • 进入 batchedUpdates

    // packages/events/ReactGenericBatching.js#L29
    let _batchedUpdatesImpl = function(fn, bookkeeping) {
      return fn(bookkeeping);
    };
    let isBatching = false;
    export function batchedUpdates(fn, bookkeeping) {
      if (isBatching) {
        // If we are currently inside another batch, we need to wait until it
        // fully completes before restoring state.
        return fn(bookkeeping);
      }
      isBatching = true;
      try {
        return _batchedUpdatesImpl(fn, bookkeeping);
      } finally {
        // Here we wait until all updates have propagated, which is important
        // when using controlled components within layers:
        // https://github.com/facebook/react/issues/1698
        // Then we restore state of any controlled component.
        isBatching = false;
        // 下面的代码其实就跟 input 控制输入 有关的
        // 我们知道在 react 当中我们通过 value 给 input 标签上面去绑定了值,直接在外部输入的值
        // 如果没有 onChange 事件来处理这个 state,它这个值输不进去的,这就是 input的控制输入 的一个概念
        // 其控制就是在这里实现的
        const controlledComponentsHavePendingUpdates = needsStateRestore();
        if (controlledComponentsHavePendingUpdates) {
          // If a controlled event was fired, we may need to restore the state of
          // the DOM node back to the controlled value. This is necessary when React
          // bails out of the update without touching the DOM.
          _flushInteractiveUpdatesImpl();
          restoreStateIfNeeded();
        }
      }
    }
    
    // packages/events/ReactControlledComponent.js#L58
    export function needsStateRestore(): boolean {
      return restoreTarget !== null || restoreQueue !== null;
    }
    
    export function restoreStateIfNeeded() {
      if (!restoreTarget) {
        return;
      }
      const target = restoreTarget;
      const queuedTargets = restoreQueue;
      restoreTarget = null;
      restoreQueue = null;
    
      restoreStateOfTarget(target);
      if (queuedTargets) {
        for (let i = 0; i < queuedTargets.length; i++) {
          restoreStateOfTarget(queuedTargets[i]);
        }
      }
    }
    
  • batchedUpdates 里面调用的方法是 handleTopLevel

    function handleTopLevel(bookKeeping) {
      let targetInst = bookKeeping.targetInst;
    
      // Loop through the hierarchy, in case there's any nested components.
      // It's important that we build the array of ancestors before calling any
      // event handlers, because event handlers can modify the DOM, leading to
      // inconsistencies with ReactMount's node cache. See #1105.
      let ancestor = targetInst;
      do {
        // 如果不存在,就push,并且跳出循环
        if (!ancestor) {
          bookKeeping.ancestors.push(ancestor);
          break;
        }
        // 如果存在了,找到 Root 挂载节点
        const root = findRootContainerNode(ancestor);
        // 不存在挂载节点,则跳出
        if (!root) {
          break;
        }
        // 存在挂载节点
        bookKeeping.ancestors.push(ancestor);
        // 这个方法之前已经分析了,找到存储的 fiber 对象
        // 在这里, 这个时候传入了是这个root,对于大部分情况来讲, HostRoot 已经没有上级的节点,会是处于react的一个应用当中
        // 也可能是会有这种情况的, 比如通过一些比较 hack 的一些方式, 我们在react应用里面再去渲染了一个新的react应用
        // 这种方法也可能是存在的,所以在这边就尝试了这么去做
        // 因为它们两个如果确实出现这种情况,那么它们的root节点是不一样的
        // 事件正常来讲是要冒泡到最外层的那个root树的最顶上的
        // 所以这种情况需要去调用这个方法,去把所有的 ancestor 给找到,并且推到这个 bookKeeping.ancestors 里面
        ancestor = getClosestInstanceFromNode(root); 
      } while (ancestor);
    
      // 对于每一个 concest,去获取它的 targetInst 对大部分情况, 其实就是我们这个 targetInst 
      // 就是我们这个事件触发的那一个节点 event.target 对应的那个fiber对象
      for (let i = 0; i < bookKeeping.ancestors.length; i++) {
        targetInst = bookKeeping.ancestors[i];
        runExtractedEventsInBatch(
          bookKeeping.topLevelType,
          targetInst,
          bookKeeping.nativeEvent,
          getEventTarget(bookKeeping.nativeEvent),
        );
      }
    }
    
    • 进入 findRootContainerNode
      function findRootContainerNode(inst) {
        // TODO: It may be a good idea to cache this to prevent unnecessary DOM
        // traversal, but caching is difficult to do correctly without using a
        // mutation observer to listen for all DOM changes.
        // 向上找
        while (inst.return) {
          inst = inst.return;
        }
        // 如果不是 HostRoot 则失败
        if (inst.tag !== HostRoot) {
          // This can happen if we're in a detached tree.
          return null;
        }
        // 找到 react 应用挂载的 dom节点
        return inst.stateNode.containerInfo;
      }
      
    • 进入 runExtractedEventsInBatch
      export function runExtractedEventsInBatch(
        topLevelType: TopLevelType,
        targetInst: null | Fiber,
        nativeEvent: AnyNativeEvent,
        nativeEventTarget: EventTarget,
      ) {
        // 获取所有事件
        const events = extractEvents(
          topLevelType,
          targetInst,
          nativeEvent,
          nativeEventTarget,
        );
        runEventsInBatch(events);
      }
      
      // 生成事件对象
      function extractEvents(
        topLevelType: TopLevelType,
        targetInst: null | Fiber,
        nativeEvent: AnyNativeEvent,
        nativeEventTarget: EventTarget,
      ): Array<ReactSyntheticEvent> | ReactSyntheticEvent | null {
        let events = null;
        // 调用每一个plugin, 在内部,调用 possiblePlugin.extractEvents
        for (let i = 0; i < plugins.length; i++) {
          // Not every plugin in the ordering may be loaded at runtime.
          const possiblePlugin: PluginModule<AnyNativeEvent> = plugins[i];
          if (possiblePlugin) {
            const extractedEvents = possiblePlugin.extractEvents(
              topLevelType,
              targetInst,
              nativeEvent,
              nativeEventTarget,
            );
            // 如果存在,则插入到对象中
            if (extractedEvents) {
              events = accumulateInto(events, extractedEvents);
            }
          }
        }
        return events;
      }
      
      let eventQueue: ?(Array<ReactSyntheticEvent> | ReactSyntheticEvent) = null;
      export function runEventsInBatch(
        events: Array<ReactSyntheticEvent> | ReactSyntheticEvent | null,
      ) {
        if (events !== null) {
          eventQueue = accumulateInto(eventQueue, events);
        }
      
        // Set `eventQueue` to null before processing it so that we can tell if more
        // events get enqueued while processing.
        const processingEventQueue = eventQueue;
        eventQueue = null;
      
        if (!processingEventQueue) {
          return;
        }
        // 对这个 processingEventQueue, 可能是数组,也可能只有一个event的这个内容
        // 对它调用了这个 executeDispatchesAndReleaseTopLevel 方法
        forEachAccumulated(processingEventQueue, executeDispatchesAndReleaseTopLevel);
        invariant(
          !eventQueue,
          'processEventQueue(): Additional events were enqueued while processing ' +
            'an event queue. Support for this has not yet been implemented.',
        );
        // This would be a good time to rethrow if any of the event handlers threw.
        rethrowCaughtError();
      }
      
      • 进入 accumulateInto
        // 把两个值(数组)合并,形成一个数组
        function accumulateInto<T>(
          current: ?(Array<T> | T),
          next: T | Array<T>,
        ): T | Array<T> {
          invariant(
            next != null,
            'accumulateInto(...): Accumulated items must not be null or undefined.',
          );
        
          if (current == null) {
            return next;
          }
        
          // Both are not empty. Warning: Never call x.concat(y) when you are not
          // certain that x is an Array (x could be a string with concat method).
          // 合并两个数组
          if (Array.isArray(current)) {
            if (Array.isArray(next)) {
              current.push.apply(current, next);
              return current;
            }
            current.push(next);
            return current;
          }
        
          if (Array.isArray(next)) {
            // A bit too dangerous to mutate `next`.
            return [current].concat(next);
          }
        
          return [current, next];
        }
        
      • 进入 forEachAccumulated
        // 对于传进来的一个数组,判断它是否是一个数组
        // 如果是数组就会去对每一项调用这个 callback
        // 如果它不是一个数组,就直接调用这个callback传入这个值就可以了
        function forEachAccumulated<T>(
          arr: ?(Array<T> | T),
          cb: (elem: T) => void,
          scope: ?any,
        ) {
          if (Array.isArray(arr)) {
            arr.forEach(cb, scope);
          } else if (arr) {
            cb.call(scope, arr);
          }
        }
        
      • 进入 executeDispatchesAndReleaseTopLevel
        const executeDispatchesAndReleaseTopLevel = function(e) {
          return executeDispatchesAndRelease(e);
        };
        
        const executeDispatchesAndRelease = function(event: ReactSyntheticEvent) {
          if (event) {
            executeDispatchesInOrder(event);
        
            if (!event.isPersistent()) {
              event.constructor.release(event);
            }
          }
        };
        
        // packages/events/EventPluginUtils.js#L76
        // 最后真正调用这个事件的地方,其实它里面整个过程会非常的复杂,有各种各样的函数的嵌套调用
        // 其实里面可能有将近一半的函数都是工具类型的函数,注意阅读代码时,别被绕进去
        export function executeDispatchesInOrder(event) {
          // 这边获取了 dispatchListeners 以及 dispatchInstances 这两个数据
          // 这两个数据都来自 event 对象,上面会挂载了一个叫 _dispatchListeners 和 _dispatchInstances
          // 这两个东西都是数组,并且是一一对应的关系
          const dispatchListeners = event._dispatchListeners;
          const dispatchInstances = event._dispatchInstances;
          // 忽略
          if (__DEV__) {
            validateEventDispatches(event);
          }
          // 然后,它去判断一下是否是一个数组, 如果是一个数组对它进行一个遍历
          if (Array.isArray(dispatchListeners)) {
            for (let i = 0; i < dispatchListeners.length; i++) {
              // 并且判断一下 event 是否已经 isPropagationStopped,就是我们已经停止冒泡了
              // 如果是的话,我们就直接 break
              if (event.isPropagationStopped()) {
                break;
              }
              // Listeners and Instances are two parallel arrays that are always in sync.
              executeDispatch(event, dispatchListeners[i], dispatchInstances[i]);
            }
          // 不是数组,但是存在
          } else if (dispatchListeners) {
            // 调用这个方法
            executeDispatch(event, dispatchListeners, dispatchInstances);
          }
          // 重置
          event._dispatchListeners = null;
          event._dispatchInstances = null;
        }
        
        • 进入 executeDispatch
          function executeDispatch(event, listener, inst) {
            const type = event.type || 'unknown-event';
            event.currentTarget = getNodeFromInstance(inst); // 从 inst 上获取 node 节点
            invokeGuardedCallbackAndCatchFirstError(type, listener, undefined, event); // 这里回调最终被触发调用
            event.currentTarget = null;
          }
          
          • 进入 invokeGuardedCallbackAndCatchFirstError
            // packages/shared/ReactErrorUtils.js#L67
            export function invokeGuardedCallbackAndCatchFirstError<
              A,
              B,
              C,
              D,
              E,
              F,
              Context,
            >(
              name: string | null,
              func: (a: A, b: B, c: C, d: D, e: E, f: F) => void,
              context: Context,
              a: A,
              b: B,
              c: C,
              d: D,
              e: E,
              f: F,
            ): void {
              // 这个函数之前遇到过,目前不再展开
              invokeGuardedCallback.apply(this, arguments);
              if (hasError) {
                const error = clearCaughtError();
                if (!hasRethrowError) {
                  hasRethrowError = true;
                  rethrowError = error;
                }
              }
            }
            
  • 到这里为止,这边的 listener 就已经被调用了

  • 我们真正的每一个节点上面,如果有绑定这个事件,它就会调用它的一个回调

  • 这就是事件触发的整个流程, 非常的繁琐,有各种各样的方法,嵌套的调用

【React】React18核心源码解读 阅读详情

相关推荐

React源码分析8-状态更新的优先级机制

react的优先级机制在源码中并不是一个独立的,解耦的模块,而是涉及到了react整体运行的方方面面,最后回归整理下优先级机制在源码中的使用,让大家对优先级机制有一个更加整体的认知。时间分片。涉及到任务打断、根据优先级计算分片时长setState 生成 Update 对象。每个 Update 对象里面都有一个 lane 属性,代表此次更新的优先级高优先级任务打断低优先级任务。每一次调度都会对正在进行任务和当前任务最高优先级做比较,如果不相等,就代表有高优先级任务进来,需要打断当前正在的任务。

It_kc的博客 695

在react中使用event(事件对象),如何使用,注意事项和react中如何定义和使用函数?

定义使用方式和function一样的区别在于箭头函数没有自己的this指向,只会向上继承父级的this指向,如果父级没有this指向还会继续向上检索执行后的效果。

Youyzq的博客 2399

终端渲染原理:React+Yoga+Canvas高性能实现解析

终端渲染并非简单的DOM输出,而是融合声明式UI、增量布局与跨平台文本绘制的精密系统。其核心在于将日志流结构化处理、解耦布局计算与像素绘制,并通过Yoga引擎实现高精度等宽字体测量与零GC布局。相比传统DOM方案,该架构显著规避强制同步布局(Layout Thrashing)和V8垃圾回收压力,使高频日志场景下仍保持60fps流畅滚动。典型应用场景包括IDE插件、AI编程助手桌面端及CLI可视化面板——尤其适用于需兼顾响应速度与视觉一致性的Electron或Tauri项目。本文深入剖析从React Reco

weixin_30372371的博客 436

React中events的踩坑指南

本文介绍react项目中如何创建events来进行跨组件事件触发重点讲解自定义参数传递,和如何拿到最新的state值🌟。

分享前端知识,记录前端进阶 908

reactphp-eventsource:简单的EventSource客户端实现,可使用ReactPHP消耗流式服务器发送的事件(SSE)

线索/ reactphp-eventsource 事件驱动的EventSource客户端,从任何基于HTML5服务器发送的事件(SSE)服务器接收流消息。 注意:该项目处于早期Alpha阶段! 随时报告您遇到的任何问题。 目录 快速入门示例 ,您可以使用以下代码从任何服务器发送事件(SSE)服务器端点流式传输消息: $ loop = Factory :: create (); $ es = new Clue \ React \ EventSource \ EventSource ( 'https://example.com/stream.php' , $ loop ); $ es -> on ( 'message' , function ( Clue \ React \ EventSource \ MessageEvent $ message ) { //$data = j

《AI大模型评测与智能测试》

第三篇“实践蓝图”(第6-7章)着眼于测试领域AI应用,重点介绍了RAG、AI Agent开发及构建端到端智能测试系统的方法。本书聚焦AI时代的“质量”核心,以“AI赋能测试”(AI For Testing)与“AI模型评测”(Testing For AI)为双轮驱动。内容不仅深入剖析如何科学度量AI大模型的内在质量,还系统讲解如何利用AI赋能传统软件测试,帮助读者贯通从模型原理、评测到应用开发的全链路知识。

酌沧 1142

React16源码: React中event事件对象的创建过程源码实现

目前为止,通过 ChangeEventPlugin 来了解了整个 event 对象的处理过程。这一套东西只是非常适合react,在其他框架要使用这类event库,会有很大的成本。react 团队把整个事件系统去重新抽象的这么一个过程,而且设计的超级复杂。以上,生产 event 对象,然后去挂载它的事件,这个过程是非常的复杂的。但每一个 plugin 或多或少有一些自己的一些区别,这里不再赘述。我们专门专注下 changeEvent 的这个方法。后续其他的类似事件的处理逻辑到后面都是一样的。

Wang的专栏 1656

android广告SDK原理详解(附源码)

广大的开发者吃糠咽菜开发了一两款APP,获取了一些流量后自然就会想到流量变现,一般情况下大家会选择到百度联盟或者Google Admob这些广告服务提供商注册开发者账号,在自己的APP里面加上几行广告位代码,集成他们的广告SDK包。然后谋事在人,成事在天,只有每天看报表查查收益。   也有一些开发者为了能够提高自己的变现水平,同时接入了好几家的sdk,哪家出的收益高一点我就出哪家的广告,但是这

rmcgogo的博客 1万+

React16源码: React中event事件中batchedUpdates, controlled inputs以及事件绑定dispatchEvent源码实现

前面把 react 当中事件从开始注入到事件的监听,到把整个事件对象给它构造出来,最后去触发这个事件的整体的流程,还有一些重要的内容,如:batchedUpdates,controlled inputs 如何回调,关于事件绑定dispatchEvent

Wang的专栏 1136

LD14激光雷达与STM32F103C8T6(标准库)获取雷达360度数据(1)

LD14激光雷达与STM32F103C8T6(标准库)获取雷达360度数据(1)

qq_54206194的博客 3900

鱼眼相机矫正实战:除了棋盘格,试试横向展开和经纬度法(OpenCV4/C++实现对比)

本文对比了鱼眼相机矫正的三种主流方法:棋盘格标定法、横向展开法和经纬度法,通过OpenCV4/C++实战代码解析,帮助开发者在自动驾驶、VR全景拼接和安防监控等场景中选择最适合的技术方案。重点分析了各方法的精度、处理速度和适用场景,为工程实践提供实用指导。

weixin_29032489的博客 285

React v16源码之setState触发的更新流程

前言 实际上在之前React v15相关原理的文章中就setState的几个特点有了较为清晰的梳理,虽然在React v16中引入了Fiber架构,但是setState整体的处理逻辑以及使用并没有什么区别,只是底层处理不同而已,为什么还有梳理v16 setState相关的逻辑呢?侧重点不同,本文不会主要去看setState的一些特点的形成原因(在前面的文章中就已经说明了再说就没有意义),主要想通过setState去看视图更新机制。 setState 以简单的实例来梳理整个源码运行流程,实例如下: c

玉案轩窗的博客 1343

在trae CN中使用conda环境(已解决)

Trae CN 是字节跳动于 2025 年 3 月 3 日发布的国内首个 AI 原生集成开发环境(AI IDE)123。

zwzscnds的博客 6940

React-Tap-Event-Plugin与React版本兼容性:选择正确版本的终极清单

还在为移动端React应用的300ms点击延迟而烦恼吗?😅 React-Tap-Event-Plugin正是解决这个问题的终极工具!本文将为你提供一份完整的版本兼容性指南,帮助你轻松选择正确的插件版本,确保你的React应用在移动设备上拥有流畅的触控体验。 ## 为什么需要React-Tap-Event-Plugin?🤔 移动端开发中,浏览器为了区分单击和双击操作,会在触摸事件后等待300

gitblog_00603的博客 861

STM32+4G模块+MQTT协议:从零构建稳定可靠的物联网通讯链路

本文详细介绍了如何利用STM32、4G模块和MQTT协议构建稳定可靠的物联网通讯链路。通过硬件选型、AT指令配置和MQTT连接实战,帮助开发者实现低功耗、高可靠性的数据传输,特别适合户外和移动场景应用。

weixin_29191081的博客 334

【生成数据集EXCEL文件】使用生成对抗网络GAN生成数据集:输出生成数据集EXCEL

生成器的任务是接收随机噪声向量作为输入,并尽可能生成与真实数据相似的样本,而判别器则是一个二分类器,旨在区分输入的样本是来自生成器生成的,还是来自真实数据。在训练过程中,生成器和判别器通过对抗学习的方式相互博弈,生成器的目标是生成足以欺骗判别器的样本,而判别器的目标则是尽可能准确地判断样本的真实性。GAN网络在多个领域展现了强大的能力,能够生成与训练数据分布相似的新数据,包括但不限于图像生成、图像风格转换、超分辨率、数据增强、视频生成、自然语言处理、医学图像处理以及游戏与虚拟现实等。

hustef的博客 1522

从0到1理解React-Tap-Event-Plugin:新手友好的概念入门

在移动端Web开发中,你是否遇到过点击按钮时感觉有延迟?这就是著名的"300ms点击延迟"问题!😊 **React-Tap-Event-Plugin** 正是为了解决这个问题而生的终极解决方案。这个轻量级插件能够为你的React应用提供即时触摸响应,消除恼人的延迟,提升用户体验。 ## 📱 什么是300ms点击延迟? 当用户在移动设备上点击屏幕时,浏览器会等待大约300毫秒才触发`clic

gitblog_00530的博客 855

华为OD技术面八股文_C++_01

汇总华为OD技术面C++方向八股文真题,并提供详细解答。

qq_45776114的博客 1889

bootstraptable append 没有events事件_从源码看React16事件机制-事件合成

前言在事件注册部分,你有可能有下面几个困扰: 为什么所有注册的事件的回调都是一个dispatchEvent,它到底是怎么运作的呢? 事件注册部分从头到尾都没有用到我们传入的事件回调,仅仅对我们想要绑定的事件做了一些处理(绑定依赖等等),那React是怎么正确执行我们指定的回调的呢?别急,在继续讲下去之前,我们要先聊一下React16与之前版本的最大区别:Fiber架构对于16之前的React来...

weixin_36353849的博客 111

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

制作渐变背景生成器 - React State和Event教程(1)

好,这个渐变背景生成器现在就把它写完了,你也可以把它扩展一下,比如可以自定义渐变的角度,选择多个颜色等等。今天用它把 React 的 state 和 event 两个概念讲了一下,也很简单,是不是?Props 的值是只读不可变的,改变它的值不会引起组件更新。自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。

2401_83621499的博客 1011

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

快速免费XIVLauncher:FF14第三方启动器完整指南

XIVLauncher(简称 XL)是一款免费开源的 FF14 第三方启动器,定位是比官方启动器更快的替代方案:记住登录凭证、补丁断点续传、内置 Dalamud(游戏内社区插件框架)生态。从安装到进阶用法,这份指南一次讲完。 ## 一、靠谱吗:先交代项目背景 先说这个项目是谁做的、可信度如何,再谈功能。 - **开源情况**:采用 GPL v3 协议开源,从启动器核心到补丁系统([src/X

gitblog_00560的博客 481

React与Unity WebGL双向通信:原理、实现与性能优化

在现代Web开发中,前端框架与3D渲染引擎的深度整合已成为构建复杂可视化应用的关键需求。其核心原理在于建立JavaScript与WebAssembly之间的高效通信桥梁,通过事件驱动与消息传递机制实现数据同步。这一技术方案的核心价值在于能够将React声明式UI的高效状态管理与Unity强大的实时渲染能力相结合,从而突破传统Web应用在交互体验上的局限。在实际工程实践中,开发者常需处理序列化、性能优化与跨平台兼容性等挑战,尤其是在数字孪生、在线教育、互动营销等需要高保真3D展示与复杂业务逻辑并行的应用场景中

蓝色妖姬 1022

源码解析:深入理解 Shadow DOM 与事件穿透

本文解析了Shadow DOM在事件处理中的关键机制及解决方案。当事件从Shadow DOM内部冒泡时,浏览器会对事件目标进行重定向,导致React等框架的事件代理失效。AutoForm SDK通过使用composed:true的自定义事件和composedPath()方法,成功解决了跨Shadow DOM的事件交互问题。文章揭示了事件重定向原理,并展示了如何实现精准的事件捕获,为Web Component开发提供了重要参考。

lxjtsoftware的专栏 568

Material UI(MUI)React 浮动操作按钮 Fab 组件:用法、尺寸、动画与源码实现全解

本篇基于 MUI 仓库中 Floating Action Button(浮动操作按钮,下称 FAB)的官方文档([floating-action-button.md](https://link.gitcode.com/i/5c229ad61022d37b3d101274366579fd))及其配套示例、源码与测试编写。读完你将掌握:如何在 React 项目中正确使用 `@mui/material/

gitblog_00674的博客 732
上一篇: React16源码: React中event事件监听绑定的源码实现
下一篇: React16源码: React中event事件对象的创建过程源码实现
Wang's Blog
博客等级 码龄12年 7434粉丝 2421原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wang's Blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值