欢迎学习交流!!!
持续更新中…
'流’这个名词在JS中随处可见。像DOM事件流、React中的数据流等等。
‘流’是一种有方向的数据;事件流,是页面接受事件的顺序。
当某个事件触发时,文档根节点最先接受到事件,然后根据DOM树结构向具体绑定事件的元素传递。该阶段为父元素截获事件提供了机会。
事件传递路径为:
window —> document —> body —> button
具体元素已经捕获事件,之后事件开始向根节点冒泡。
该阶段的开始即是事件的开始,根据DOM树结构由具体触发事件的元素向根节点传递。
事件传递路径:
button —> body —> document —> window
选择监听事件的阶段:
使用addEventListener
函数在事件流的的不同阶段监听事件,
DOM.addEventListener(‘事件名称’,handleFn,Boolean);
第三个参数Boolean即代表监听事件的阶段:
为true时,在捕获阶段监听事件;
为false时,在冒泡阶段监听事件
事件冒泡和事件捕获:
HTML DOM中有两种事件传播方式,冒泡和捕获方式。事件传播是一种在事件发生时定义元素顺序的方法
- 冒泡:从里到外
- 捕获:从外到里
React在事件绑定时有一套自身的机制,就是合成事件。
react中事件绑定:
<div className="dome" onClick={this.handleClick}>
普通的事件绑定:
<div class="dome" onclick="handleClick()">
React合成事件机制:
React并不是将click事件直接绑定在dom上面,而是采用事件冒泡的形式冒泡到document上面,然后React将事件封装给正式的函数处理运行和处理。
若react事件绑定在真实的DOM节点上,一个DOM节点绑定过多事件处理函数,整个页面的响应和内存占用可能都会受到很大的影响。
React为了避免这类DOM事件的滥用,同时屏蔽底层不同浏览器之间的时间系统的差异,出现了一个中间层——SyntheticEvent。
SyntheticEvent
(负责所有事件合成)dispatchEvent
将封装的事件内容交由真正的处理函数执行
注:
由于class的方法默认不会绑定this,若没有绑定this,调用this的时候为undefined
export default class App extends React.Component {
show(){
console.log(this)
}
render (){
return <button onClick={ this.show }>点击</button>
}
解决方法:
export default class App extends React.Component {
constructor(props) {
super(props);
this.show = this.show.bind(this);
}
show(){
console.log(this)
}
render() {
return <button onClick={ this.show }>点击</button>
;
}
}
export default class App extends React.Component {
show(){
console.log(this);
}
render() {
return <button onClick={this.show.bind(this)}>点击</button>
}
}
export default class App extends React.Component {
show(){
console.log(this);
}
render() {
return <button onClick={()=>this.show()}>点击</button>
}
}
react封装了大多数的原生事件,但也有个例:
由于原生事件需要绑定在真实DOM上,所以一般是在componentDidMount
阶段/ref
的函数执行阶段进行绑定操作,在componentWillUnmount
阶段进行解绑操作以避免内存泄漏。
若业务场景中需要混合使用合成事件和原生事件,使用过程中需注意:
class Demo extends React.Component {
componentDidMount() {
const $this = ReactDOM.findDOMNode(this)
$this.addEventListener('click', this.onDOMClick, false)
}
onDOMClick = evt => {
console.log('dom event')
}
onClick = evt => {
console.log('react event')
}
render() {
return (
<div onClick={this.onClick}>Demo</div>
)
}
}
分析:首先DOM事件监听器被执行,然后事件继续冒泡至document,合成事件监听器被执行。
如果在onDOMClick
中调用evt.stopPropagation()
方法,由于DOM事件被阻止冒泡了,无法到达document,所以合成事件自然不会被触发
export default class Test extends Component {
componentDidMount() {
const $parent = ReactDOM.findDOMNode(this);
const $child = $parent.querySelector('.child');
$parent.addEventListener('click', this.onParentDOMClick, false)
$child.addEventListener('click', this.onChildDOMClick, false)
}
onParentDOMClick = evt => {
console.log('parent dom event')
}
onChildDOMClick = evt => {
console.log('child dom event')
}
onParentClick = evt => {
console.log('parent react event')
}
onChildClick = evt => {
evt.stopPropagation();
console.log('child react event')
}
render() {
return (
<div onClick={this.onParentClick}>
<div className="child" onClick={this.onChildClick}>
Demo
</div>
</div>
)
}
}
在onChildClick
中调用evt.stopPropagtion()
,合成事件不会被触发是因为React给合成事件封装的stopPropagation
函数调用时给自己加了isPropagationStopped
的标记位来确定后续监听器是否执行。
参考文章:
- React-合成事件和原生事件的区别
- React合成事件和DOM原生事件的区别