React源码解析18(7)------ 实现事件机制(onClick事件)

这篇具有很好参考价值的文章主要介绍了React源码解析18(7)------ 实现事件机制(onClick事件)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

摘要

在上一篇中,我们实现了useState的hook,但由于没有实现事件机制,所以我们只能将setState挂载在window上。
而这一篇主要就是来实现事件系统,从而实现通过点击事件进行setState。

而在React中,虽然我们是将事件绑定在JSX上的某个元素上,但是其实最终的执行者是最外层的容器。
也就是说React利用了冒泡的机制,将所有事件都冒泡到了最外层容器上,从而创建合成事件,在对相应的事件执行。

所以在实现事件机制之前,我们先将准备好的JSX进行修改:

function App() {
  const [name, setName] = useState('kusi','key');
  const [age, setAge] = useState(20)
  const click1 = () => {
    console.log(name)
    setName(name + '1')
  }
  const click2 = () => {
    console.log(age)
    setAge(age + 1)
  }
  return jsx("div", {
    ref: "123",
    onClick: click1,
    children: jsx("span", {
      children: name + age,
      onClick: click2
    })
  });
}

1.实现initEvent方法

刚才我们说了,在React中,事件的执行者是最外层的容器,也就是说我们需要给最外层的容器绑定一个事件,用来初始化。

export const initEvent = (root, eventType) => {
  root.addEventListener(eventType, (e) => {
    dispatchEvent()
  })
}

而我们可以在最开始的时候,调用initEvent。最开始也就是createContainer方法里面:

function createContainer(root) {
  initEvent(root, 'click')
  const hostRootFilber = new FilberNode(HostRoot, {}, '')
  return new FilberRootNode(root, hostRootFilber)
}

这里我们先实现click事件。

2.给所有DOM绑定props

我们思考一下,对于所有的事件,一定是在对应组件的Props里面,而我们要在dom上拿到对应的事件,那么就要将props属性同步给dom。
而真实DOM是在completeWork阶段生成的,所以我们需要实现一个方法,用来给dom绑定props属性:

function addPropsToDOM(element, props) {
  element['__props'] = props
}

在completeWork阶段,调用该方法:

export const completeWork = (filberNode) => {
  const tag = filberNode.tag
  switch (tag) {
    case HostComponent: {
      if(filberNode.stateNode !== null){
        //更新
        addPropsToDOM(filberNode.stateNode, filberNode.pendingProps)
      }else{
        completeHostComponent(filberNode)
      }
      break;
    }
function completeHostComponent(filberNode) {
  const type = filberNode.type;
  const element = document.createElement(type);
  addPropsToDOM(element, filberNode.pendingProps)
  filberNode.stateNode = element;
  const parent = filberNode.return;
  if(parent && parent.stateNode && parent.tag === HostComponent) {
    parent.stateNode.appendChild(element)
  }
  completeWork(filberNode.child)
}

此时可以打印看一下stateNode中的element,是否已经有__props属性了:

React源码解析18(7)------ 实现事件机制(onClick事件),react.js,javascript,前端

3.收集所有事件

现在所有的DOM已经有了对应的事件,现在我们需要将所有的事件收集起来:
收集的过程就是,当前点击的元素,到最外层容器录过的所有事件。
所以我们需要三个参数:当前点击的元素,容器,事件类型。

由于在React中,事件分为两种,比如onClick和onClickCapture。所以我们用两个集合来收集这两种事件。

function collectEvent(event, root, eventType) {
  const bubble = [];
  const capture = [];

  while(event !== root){
    const eventProps = event['__props'];
    if(eventType === 'click'){
      const click = eventProps['onClick'];
      const clickCapture = eventProps['onClickCapture'];
      if(click){
        bubble.push(click);
      }
      if(clickCapture){
        capture.unshift(clickCapture)
      }
    }
    event = event.parentNode;
  }
  return {bubble, capture}
}

然后我们在dispatchEvent中进行调用:

function dispatchEvent(root, eventType, e) {
  const {bubble, capture} = collectEvent(e.target, root, eventType)
  console.log(bubble, capture);
}

我们看一下打印结果:
React源码解析18(7)------ 实现事件机制(onClick事件),react.js,javascript,前端
可以看到在bubble中,已经将方法保存下来了。

4.创建合成事件对象

我们现在已经收集了这么多方法,按理说也该去执行了。但是有一个问题, 我们创建了bubble和capture。只是用来模仿浏览器的冒泡和捕获,也就是并非是真正的冒泡捕获。

最终执行所有事件的还是root,所以我们要创建一个新的event,用来代替浏览器的event。

在这个方法中,我们用一个标志位__stopPropgation来决定是否冒泡。如果在外面调用“e.stopPropgation”,我们将这个标志位置位true。

function createSyntheticEvent(e) {
  const syntheticEvent = e;
  syntheticEvent.__stopPropgation = false;
  const originStopPropgation = e.stopPropagation;

  syntheticEvent.stopPropagation = () => {
    syntheticEvent.__stopPropgation = true;
    if( originStopPropgation ) {
      originStopPropgation()
    }
  }
  return syntheticEvent;
}
}

在dispatchEvent中进行调用:

function dispatchEvent(root, eventType, e) {
  const {bubble, capture} = collectEvent(e.target, root, eventType)
  console.log(bubble, capture);
  const se = createSyntheticEvent(e)
}

4.事件调用

OK,现在我们要进行最后一步,对事件进行调用了。我们只需要对bubble和capture中的事件进行遍历调用即可,现在我们实现一个方法:

function triggerEvent(paths, se) {
  for(let i=0; i< paths.length; i++) {
    paths[i].call(null, se);
    if(se.__stopPropgation) {
      break;
    }
  }
}

然后再dispatchEvent中执行:文章来源地址https://www.toymoban.com/news/detail-651071.html

function dispatchEvent(root, eventType, e) {
  const {bubble, capture} = collectEvent(e.target, root, eventType)
  const se = createSyntheticEvent(e);
  triggerEvent(capture,se);
  if(!se.__stopPropgation) {
    triggerEvent(bubble,se)
  }
}

到了这里,关于React源码解析18(7)------ 实现事件机制(onClick事件)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • React源码解析18(1)------ React.createElement 和 jsx

    我们知道在React17版本之前,我们在项目中是一定需要引入react的。 import React from “react” 即便我们有时候没有使用到React,也需要引入。原因是什么呢? 在React项目中,如果我们使用了模板语法JSX,我们知道它要先经过babel的转译。那babel会将JSX转换成什么样子的格式呢? 可

    2024年02月13日
    浏览(26)
  • React源码解析18(2)------ FilberNode,FilberRootNode结构关系

    在上一篇,我们实现了通过JSX转换为ReactElement的方法,也看到了转换后React元素的结构。但是这个React元素,并不能很清楚的表达组件之间的关系,以及属性的处理。 所以在React内部,会将所有的React元素转换为Filber树。而这一章节,主要就是简单描述一下FilberNode的结构。 首

    2024年02月13日
    浏览(21)
  • React源码解析18(3)------ beginWork的工作流程【mount】

    OK,经过上一篇文章。我们调用了: 生成了FilberRootNode和HostRootFilber。 并且二者之间的对应关系也已经确定。 而下一步我们就需要调用render方法来讲react元素挂载在root上: 所以我们现在要实现ReactDOM.createRoot中返回的render方法。 首先我们思考一下,在React中,除了通过上面的

    2024年02月13日
    浏览(33)
  • React源码解析18(4)------ completeWork的工作流程【mount】

    经过上一章,我们得到的FilberNode已经具有了child和return属性。一颗Filber树的结构已经展现出来了。 那我们最终是想在页面渲染真实的DOM。所以我们现在要在completeWork里,构建出一颗离屏的DOM树。 之前在说FilberNode的属性时,我们提到过一个属性stateNode。它就是用来保存每个

    2024年02月13日
    浏览(27)
  • react自定义组件间的传值,if..else..判断,for循环(嵌套map使用),点击事件(Onclick),页面上事件实事传递参数(基础版)

    9.自定义组件间的传值 10.if..else..判断 11.for循环(嵌套map使用) 12.点击事件(Onclick) 13.页面上事件实事传递参数 14.关于export default function App()与export function App()的区别    export default不需要{}      import  Gallery from \\\'./Gallery.js\\\';    export function需要{}       import {Gallery} from \\\'./Galler

    2024年01月17日
    浏览(29)
  • react事件机制

    React的事件机制是React框架中非常重要的一部分,用于处理用户交互和用户界面上的事件。React的事件机制在底层使用了虚拟DOM以及合成事件来提高性能和跨浏览器兼容性。以下是关于React事件机制的详细信息: 合成事件(Synthetic Event) :React引入了合成事件的概念,以代替浏

    2024年02月07日
    浏览(30)
  • [React源码解析] React的设计理念和源码架构 (一)

    任务分割 异步执行 让出执法权 1.React的设计理念 Fiber: 即对应真实dom, 又作为分隔单元。 Scheduler: 用js实现一套时间片运行的机制, 使得requestIdleCallback()的浏览器的兼容性和触发不稳定的问题解决。 Lane: 异步调度有了, 需要细粒度的管理各个任务的优先级, 让高优先级的先执行

    2024年02月07日
    浏览(27)
  • React Native 源码分析(四)—— TurboModules JSI通信机制

    本文会详细分析React Native 新架构的TurboModules的通信过程,基于JSI的通信方式,除不会涉及Hemers引擎部分,其余代码都会详细分析,但比较简单的,不会很啰嗦,可以说是网上最完整详细的分析文章,代码通过断点截图,可以更方便查看运行的过程 1、React Native 源码分析(一)

    2024年02月22日
    浏览(38)
  • React基础源码解析

    前端魔术师卡颂的react学习视频(1 搭建项目架构_哔哩哔哩_bilibili)中提到了Rodrigo Pombo的一篇react源码教程:Build your own React 本文档分组旨在翻译和记录这篇文章的学习心得,作为react源码学习入门。 原文档目录 Step I: The createElement Function Step II: The render Function Step III: Concurr

    2024年02月06日
    浏览(24)
  • React Hooks 源码解析:useEffect

    React Hooks 源码解析(4):useEffect React 源码版本: v16.11.0 源码注释笔记:airingursb/react 1.1 为什么要有 useEffect 我们在前文中说到 React Hooks 使得 Functional Component 拥有 Class Component 的特性,其主要动机包括: 在组件之间复用状态逻辑很难 复杂组件变得难以理解 难以理解的 class 对

    2024年01月21日
    浏览(29)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包