08.事件合成机制的简单实现
具体代码演化过程请观看视频,这里呈现关键代码:
js
// event.js
import { updaterQueue, flushUpdaterQueue } from "./Component.js";
export function addEvent(dom, eventName, bindFunction) {
dom.attach = dom.attach || {}
dom.attach[eventName] = bindFunction
// 核心点 1/2: 事件绑定到document上
if(document[eventName]) return
document[eventName] = dispatchEvent;
}
function dispatchEvent(nativeEvent) {
updaterQueue.isBathingUpdate = true;
// 这里本质上是对原始事件进行了一层代理
// 核心点 2/2: 屏蔽浏览器差异
let syntheticEvent = createSyntheticEvent(nativeEvent);
let target = nativeEvent.target;
/**
* while循环是为了处理冒泡,否则执行执行下面代码就可以了:
* let eventType = `on${nativeEvent.type}`;
* let bindFunction = target.attach && target.attach[eventType];
* bindFunction && bindFunction(nativeEvent.target)
*/
while (target) {
syntheticEvent.currentTarget = target
let eventName = `on${nativeEvent.type}`;
let bindFunction = target.attach && target.attach[eventName];
bindFunction && bindFunction(syntheticEvent);
if (syntheticEvent.isPropagationStopped) {
break;
}
target = target.parentNode;
}
flushUpdaterQueue()
}
function createSyntheticEvent(nativeEvent) {
let natvieEventKeyValues = {}
for(let key in nativeEvent){
natvieEventKeyValues[key] = typeof nativeEvent[key] === 'function'? nativeEvent[key].bind(nativeEvent) : nativeEvent[key]
}
let syntheticEvent = Object.assign(natvieEventKeyValues, {
nativeEvent,
isDefaultPrevented: false,
isPropagationStopped: false,
preventDefault: function () {
this.isDefaultPrevented = true;
if (this.nativeEvent.preventDefault) {
this.nativeEvent.preventDefault();
} else {
this.nativeEvent.returnValue = false;
}
},
stopPropagation: function () {
this.isPropagationStopped = true;
if (this.nativeEvent.stopPropagation) {
this.nativeEvent.stopPropagation();
} else {
this.nativeEvent.cancelBubble = true;
}
}
})
return syntheticEvent
}react-dom.js:
js
function setPropsForDOM(dom, VNodeProps = {}) {
// ...
for (let key in VNodeProps) {
if (key === 'children') continue;
if (/^on[A-Z].*/.test(key)) {
addEvent(dom, key.toLowerCase(), VNodeProps[key])
}
// ...
}
}js
// index.js
import React from './react';
import ReactDOM from './react-dom';
class MyClassComponent extends React.Component{
counter = 0
constructor(props) {
super(props);
this.state = { count: '0' };
}
updateShowText(newText){
this.setState({
count: newText
})
}
render(){
return <div className='test-class' style={
{
color: 'red',
cursor: 'pointer',
border: '1px solid gray',
borderRadius: '6px',
display: 'inline-block',
padding: '6px 12px'
}
} onClick={ () => this.updateShowText('' + ++this.counter) }>Simple React Counter: {this.state.count}</div>
}
}
ReactDOM.render(<MyClassComponent />, document.getElementById('root'))