成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

60行代碼實現React的事件系統

開發 項目管理
本文會用60行代碼實現這兩個模塊,讓你快速了解React事件系統的原理。

大家好,我卡頌。

由于如下原因,React的事件系統代碼量很大:

  • 需要抹平不同瀏覽器的差異
  • 與內部的「優先級機制」綁定
  • 需要考慮所有瀏覽器事件

但如果抽絲剝繭會發現,事件系統的核心只有兩個模塊:

  • SyntheticEvent(合成事件)
  • 模擬實現的事件傳播機制

本文會用60行代碼實現這兩個模塊,讓你快速了解React事件系統的原理。

在線DEMO地址[1]

Demo的效果

對于如下這段JSX:

const jsx = (
<section onClick={(e) => console.log("click section")}>
<h3>你好</h3>
<button
onClick={(e) => {
// e.stopPropagation();
console.log("click button");
}}
>
點擊
</button>
</section>
);

在瀏覽器中渲染:

const root = document.querySelector("#root");
ReactDOM.render(jsx, root);

點擊按鈕,會依次打印:

click button
click section

如果在button的點擊回調中增加e.stopPropagation(),點擊后會打印:

click button

我們的目標是將JSX中的onClick替換為ONCLICK,但是點擊后的效果不變。

也就是說,我們將基于React自制一套事件系統,他的事件名的書寫規則是形如「ONXXX」的全大寫形式。

實現SyntheticEvent

首先,我們來實現SyntheticEvent(合成事件)。

SyntheticEvent是瀏覽器原生事件對象的一層封裝。兼容所有瀏覽器,同時擁有和瀏覽器原生事件相同的API,如stopPropagation()和preventDefault()。

SyntheticEvent存在的目的是抹平瀏覽器間在事件對象間的差異,但是對于不支持某一事件的瀏覽器,SyntheticEvent并不會提供polyfill(因為這會顯著增大ReactDOM的體積)。

我們的實現很簡單:

class SyntheticEvent {
constructor(e) {
this.nativeEvent = e;
}
stopPropagation() {
this._stopPropagation = true;
if (this.nativeEvent.stopPropagation) {
this.nativeEvent.stopPropagation();
}
}
}

接收「原生事件對象」,返回一個包裝對象。原生事件對象會保存在nativeEvent屬性中。

同時,實現了stopPropagation方法。

實際的SyntheticEvent會包含更多屬性和方法,這里為了演示目的簡化了

實現事件傳播機制

事件傳播機制的實現步驟如下:

  1. 在根節點綁定事件類型對應的事件回調,所有子孫節點觸發該類事件最終都會委托給「根節點的事件回調」處理。
  2. 尋找觸發事件的DOM節點,找到其對應的FiberNode(即虛擬DOM節點)
  3. 收集從當前FiberNode到根FiberNode之間所有注冊的「該事件對應回調」
  4. 反向遍歷并執行一遍所有收集的回調(模擬捕獲階段的實現)
  5. 正向遍歷并執行一遍所有收集的回調(模擬冒泡階段的實現)

首先,實現第一步:

// 步驟1
const addEvent = (container, type) => {
container.addEventListener(type, (e) => {
// dispatchEvent是需要實現的“根節點的事件回調”
dispatchEvent(e, type.toUpperCase(), container);
});
};

在入口處注冊點擊回調:

const root = document.querySelector("#root");
ReactDOM.render(jsx, root);
// 增加如下代碼
addEvent(root, "click");

接下來實現「根節點的事件回調」:

const dispatchEvent = (e, type) => {
// 包裝合成事件
const se = new SyntheticEvent(e);
const ele = e.target;

// 比較hack的方法,通過DOM節點找到對應的FiberNode
let fiber;
for (let prop in ele) {
if (prop.toLowerCase().includes("fiber")) {
fiber = ele[prop];
}
}

// 第三步:收集路徑中“該事件的所有回調函數”
const paths = collectPaths(type, fiber);

// 第四步:捕獲階段的實現
triggerEventFlow(paths, type + "CAPTURE", se);

// 第五步:冒泡階段的實現
if (!se._stopPropagation) {
triggerEventFlow(paths.reverse(), type, se);
}
};

接下來收集路徑中「該事件的所有回調函數」。

收集路徑中的事件回調函數

實現的思路是:從當前FiberNode一直向上遍歷,直到根FiberNode。收集遍歷過程中的FiberNode.memoizedProps屬性內保存的「對應事件回調」:

const collectPaths = (type, begin) => {
const paths = [];

// 不是根FiberNode的話,就一直向上遍歷
while (begin.tag !== 3) {
const { memoizedProps, tag } = begin;

// 5代表DOM節點對應FiberNode
if (tag === 5) {
const eventName = ("on" + type).toUpperCase();

// 如果包含對應事件回調,保存在paths中
if (memoizedProps && Object.keys(memoizedProps).includes(eventName)) {
const pathNode = {};
pathNode[type.toUpperCase()] = memoizedProps[eventName];
paths.push(pathNode);
}
}
begin = begin.return;
}

return paths;
};

得到的paths結構類似如下:

捕獲階段的實現由于我們是從目標FiberNode向上遍歷,所以收集到的回調的順序是:

  • [目標事件回調, 某個祖先事件回調, 某個更久遠的祖先回調 ...]

要模擬捕獲階段的實現,需要從后向前遍歷數組并執行回調。

遍歷的方法如下:

const triggerEventFlow = (paths, type, se) => {
// 從后向前遍歷
for (let i = paths.length; i--; ) {
const pathNode = paths[i];
const callback = pathNode[type];

if (callback) {
// 存在回調函數,傳入合成事件,執行
callback.call(null, se);
}
if (se._stopPropagation) {
// 如果執行了se.stopPropagation(),取消接下來的遍歷
break;
}
}
};

注意,我們在SyntheticEvent中實現的stopPropagation方法,調用后會阻止遍歷的繼續。

冒泡階段的實現

有了捕獲階段的實現經驗,冒泡階段很容易實現,只需將paths反向后再遍歷一遍就行。

總結React事件系統的核心包括兩部分:

  • SyntheticEvent
  • 事件傳播機制

事件傳播機制由5個步驟實現。

總的來說,就是這么簡單。

參考資料

[1]在線DEMO地址:

https://codesandbox.io/s/optimistic-torvalds-9ufc5?file=/src/index.js

責任編輯:姜華 來源: 魔術師卡頌
相關推薦

2019-07-25 08:20:37

代碼開發神經網絡

2022-04-15 08:07:21

ReactDiff算法

2021-12-16 06:21:16

React組件前端

2023-07-03 07:51:47

2017-03-28 21:03:35

代碼React.js

2019-11-15 15:50:41

JS代碼React前端

2014-05-26 10:07:18

Javascript俄羅斯方塊

2023-02-20 09:45:32

技術AI

2021-12-26 12:10:21

React組件前端

2024-03-01 13:49:00

數據訓練

2023-07-06 20:40:57

圣誕抽抽樂H5

2021-04-27 11:28:21

React.t事件元素

2022-03-26 22:28:06

加密通信Python

2022-04-09 09:11:33

Python

2020-12-17 08:06:33

CSS 日歷界面

2018-01-23 09:17:22

Python人臉識別

2022-06-16 10:33:14

代碼AI

2024-02-04 17:16:22

ReactVue前端

2024-03-20 09:31:00

圖片懶加載性能優化React

2017-01-11 18:44:43

React Nativ觸摸事件Android
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 日韩精品一区二区三区在线播放 | 国产美女一区二区三区 | av天天看 | 精品视频一区二区 | 欧美一级片中文字幕 | 亚洲一区久久久 | 麻豆精品久久久 | 视频在线一区二区 | 欧美精品久久 | 天天操天天天干 | 日韩在线资源 | 久久精品播放 | 91福利电影在线观看 | 女同久久另类99精品国产 | 9色视频在线 | 国产免费一区二区三区最新6 | 毛片入口 | 福利成人 | 午夜精品久久久久久久99黑人 | 欧美精品在线一区二区三区 | 在线观看成人免费视频 | 日韩精品一区二 | 亚洲网址 | 韩国理论电影在线 | 狠狠综合久久av一区二区老牛 | 精品欧美色视频网站在线观看 | 香蕉视频在线播放 | 97精品一区二区 | 青青99| 九色国产 | 久久综合影院 | 亚洲精品乱码 | 午夜网| 在线看av网址| 毛片视频网址 | 羞羞的视频免费看 | 午夜视频免费在线观看 | 国产免费一区 | 成人在线中文字幕 | 欧美一区二区三区四区视频 | 欧美在线视频一区 |