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.tagswitch (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屬性了:

在這里插入圖片描述

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);
}

我們看一下打印結果:
在這里插入圖片描述
可以看到在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中執行:

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)}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/37565.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/37565.shtml
英文地址,請注明出處:http://en.pswp.cn/news/37565.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

前后端分離------后端創建筆記(07)表單驗證

1、我輸入數據&#xff0c;然后關閉&#xff0c;重新打開會發現殘存的數據仍然保留著 2、點了這個x號&#xff0c;數據就全部被清理了 3、點這三個地方&#xff0c;數據全部都清理掉 4、這里先寫一個方法 4.1 定義一個方法 4.2 這里表單的數據在哪里&#xff0c;就是這個 4.3 …

在 Linux 中使用 cp 命令

cp 命令是 Linux 中一個重要的命令&#xff0c;你可能經常會用到它。 正如名稱所示&#xff0c;cp 代表 復制copy&#xff0c;它被用于 在 Linux 命令行中復制文件和目錄。 這是一個相對簡單的命令&#xff0c;只有幾個選項&#xff0c;但你仍有必要深入了解它。 在展示 cp …

VLLM推理流程梳理

0x0. 前言 本文在對VLLM進行解析時只關注單卡情況&#xff0c;忽略基于ray做分布式推理的所有代碼。 0x1. 運行流程梳理 先從使用VLLM調用opt-125M模型進行推理的腳本看起&#xff1a; from vllm import LLM, SamplingParams# Sample prompts. prompts ["Hello, my n…

二次封裝element-plus上傳組件,提供校驗、回顯等功能

二次封裝element-plus上傳組件 0 相關介紹1 效果展示2 組件主體3 視頻組件4 Demo 0 相關介紹 基于element-plus框架&#xff0c;視頻播放器使用西瓜視頻播放器組件 相關能力 提供圖片、音頻、視頻的預覽功能提供是否為空、文件類型、文件大小、文件數量、圖片寬高校驗提供圖片…

el-table實現懶加載(el-table-infinite-scroll)

2023.8.15今天我學習了用el-table對大量的數據進行懶加載。 效果如下&#xff1a; 1.首先安裝&#xff1a; npm install --save el-table-infinite-scroll2 2.全局引入&#xff1a; import ElTableInfiniteScroll from "el-table-infinite-scroll";// 懶加載 V…

clion2020.3配置clang-format

標題clion 啟用clang-format 文件->設置->編輯器->代碼樣式. 為了保持原有代碼風格不變&#xff0c;可以把原始的配置風格先導出&#xff0c;最好直接保存到自己的工程下&#xff0c;.clang-format是隱藏文件&#xff0c;需要用ctrlH才能看到 文件->設置->編輯…

SpringBoot復習:(45)@Component定義的bean會被@Bean定義的同名的bean覆蓋

有同名的bean需要配置&#xff1a; spring.main.allow-bean-definition-overridingtrue 否則報錯。 package cn.edu.tju.component;import org.springframework.stereotype.Component;Component public class Person {private String name;private int age;{this.name "…

室溫超導是什么?有哪些應用場景?

目錄 一、應用場景&#xff1a;二、案例分析&#xff1a; 室溫超導是指在室溫下&#xff08;即約 20C 至 30C&#xff09;實現超導現象的材料。超導是指某些材料在低溫下電阻為零的物理現象&#xff0c;室溫超導材料是超導材料的一種。室溫超導現象的發現和研究是超導領域的一個…

ChatGPT在智能游戲和游戲AI中的應用如何?

ChatGPT在智能游戲和游戲AI領域具有廣泛的應用潛力&#xff0c;可以為游戲體驗增添智能和交互性&#xff0c;同時也有助于游戲開發者創造更豐富、更引人入勝的游戲內容。以下將詳細探討ChatGPT在智能游戲和游戲AI中的應用。 ## 1. 游戲角色的智能化 在角色扮演游戲&#xff0…

103.216.154.X服務器出現漏洞了有什么辦法?

服務器出現漏洞是一種嚴重的安全風險&#xff0c;需要及時采取措施來應對。以下是一些常見的應對措施&#xff1a; 及時更新補丁&#xff1a;確保服務器上的操作系統、應用程序和軟件都是最新版本&#xff0c;并及時應用相關的安全補丁&#xff0c;以修復已知的漏洞。 強化訪問…

OpenHarmony Meetup 廣州站 OpenHarmony正當時—技術開源

招募令 OpenHarmony Meetup 廣州站 火熱招募中&#xff0c;等待激情四射的開發者&#xff0c;線下參與OpenHarmonyMeetup線下交流 展示前沿技術、探討未來可能、讓你了解更多專屬OpenHarmony的魅力 線下參與&#xff0c;先到先得,僅限20個名額&#xff01; 報名截止時間8月23日…

【云原生】Docker 詳解(三):Docker 鏡像管理基礎

Docker 詳解&#xff08;三&#xff09;&#xff1a;Docker 鏡像管理基礎 1.鏡像的概念 鏡像可以理解為應用程序的集裝箱&#xff0c;而 Docker 用來裝卸集裝箱。 Docker 鏡像含有啟動容器所需要的文件系統及其內容&#xff0c;因此&#xff0c;其用于創建并啟動容器。 Dock…

-L和-rpath-link和-rpath

知識點 現代連接器在處理動態庫時將鏈接時路徑&#xff08;Link-time path&#xff09;和運行時路徑&#xff08;Run-time path&#xff09;分開,用戶可以通過-L指定連接時庫的路徑&#xff0c;通過-R&#xff08;或-rpath&#xff09;指定程序運行時庫的路徑&#xff0c;大大提…

Go學習-Day1

Go學習-Day1 個人博客&#xff1a;CSDN博客 打卡。 Go語言的核心開發團隊&#xff1a; Ken Thompson (C語言&#xff0c;B語言&#xff0c;Unix的發明者&#xff0c;牛人)Rob Pike(UTF-8發明人)Robert Griesemer(協助HotSpot編譯器&#xff0c;Js引擎V8) Go語言有靜態語言的…

MongoDB安裝

文章目錄 MongoDB安裝設置yum源安裝指定版本的mongodb配置文件連接MongoDB的工具MongoDBCompass MongoDB安裝 設置yum源 [rootWDQCVM sbin]# vim /etc/yum.repos.d/mongodb-org-6.0.repo [mongodb-org-6.0] nameMongoDB Repository baseurlhttps://repo.mongodb.org/yum/red…

文件預覽/下載方式:通過二進制流(Blob)下載、或者通過文件Url下載

一、 通過二進制流&#xff08;Blob&#xff09;下載 1 、API請求時候帶上類型 /*** 文件--下載* */ export function download(fphm) {return axios({url: "/ynpst/download-invoice?fphm" fphm,method: get,responseType: blob}) }2、文件預覽和下載 /*** 預覽…

JavaScript如何執行語句

目錄 語法/詞法分析 預編譯 解釋執行 預編譯什么時候發生 js運行三步曲 預編譯前奏 預編譯步驟 鞏固基礎練習 語法/詞法分析 按語句塊的粒度解析成抽象語法樹 ,分析該js腳本代碼塊的語法是否正確&#xff0c;如果出現不正確&#xff0c;則向外拋出一個語法錯誤&#x…

第4章:決策樹

停止 當前分支樣本均為同一類時&#xff0c;變成該類的葉子節點。當前分支類型不同&#xff0c;但是已經沒有可以用來分裂的屬性時&#xff0c;變成類別樣本更多的那個類別的葉子節點。當前分支為空時&#xff0c;變成父節點類別最多的類的葉子節點。 ID3 C4.5 Cart 過擬合 缺…

文本挖掘 day5:文本挖掘與貝葉斯網絡方法識別化學品安全風險因素

文本挖掘與貝葉斯網絡方法識別化學品安全風險因素 1. Introduction現實意義理論意義提出方法&#xff0c;目標 2. 材料與方法2.1 數據集2.2 數據預處理2.3 關鍵字提取2.3.1 TF-IDF2.3.2 改進的BM25——BM25WBM25BM25W 2.3.3 關鍵詞的產生(相關系數) 2.4 關聯規則分析2.5 貝葉斯…

css冒號對齊

實現后的樣式效果 實現方式 html&#xff1a; <el-col v-if"item.showInSingle ! false" :span"6" style"padding: 4px 0"><label>{{ item.label }}&#xff1a;</label><span v-if"singleData[item.prop] ! 0 &…