Last updated
Last updated
在 JSX 中直接绑定的事件,如
这里的 handleClick
事件就是合成事件。
VirtualDOM 在内存中是以 对象 的形式存在,React 基于 VirtualDOM 实现了一个 SyntheticEvent(合成事件)层,我们所定义的事件处理器会接收到一个 SyntheticEvent 对象的实例(比如handleChange(reactEvent)
),且与原生的浏览器事件有同样的接口。
注意:React 使用事件委托机制,会将所有的事件都绑定在最外层(document
)元素上,依赖事件的冒泡机制完成委派,在冒泡阶段处理事件,不支持捕获阶段处理事件。
推荐:阻止合成事件间的冒泡,用 e.stopPropagation() 。
通过 JS 原生代码绑定的事件,如:
Q:为什么有时候还需要原生事件?
Q:在什么生命周期才可以绑定原生事件?
A:组件挂载完成之后,即 componentDidMount。
强制:一定要在组件卸载(componentWillUnmount)时手动移除,否则很可能出现内存泄漏的问题,而合成事件不需要,因为 react 内部已经帮你自动处理了。
注意:合成事件中阻止事件冒泡是没办法阻止原生事件的冒泡。即使是 reactEvent.nativeEvent.stopPropagation()。
reactEvent 是封装好的事件,它是在 document 的回调里进行封装,并执行回调的。而原生的监听,在document 接收到冒泡时早就执行完了。reactEvent.nativeEvent.stopPropagation()
方法实际上是在最外层节点上调用了原生的 stopPropagation, 只阻止了 document 的冒泡。
注意:原生事件中阻止冒泡是可以阻止合成事件的冒泡。
推荐:阻止合成事件与最外层 document 上的事件间的冒泡,用e.nativeEvent.stopImmediatePropagation() 。
推荐:阻止合成事件与除最外层 document 上的原生事件上的冒泡,通过判断 e.target 来避免。
对于 合成事件 根据组件事件绑定的创建时间主要有两类方法:
渲染时绑定主要有三种:
注意:这种方法有一个潜在的性能问题:当组件每次重新渲染时,都会有一个新的函数创建。
但是在真正的开发场景中,由此引发的性能问题往往不值一提(除非是大型组件消费类应用或游戏)。
这种方法其实和第一种类似,我们可以利用ES6 箭头函数 隐式 绑定 this:
注意:这种方法与第一种方法一样,同样存在潜在的性能问题。
推荐:函数式组件优先使用箭头函数隐式绑定this。
函数绑定运算符是并排的两个双冒号( :: ),双冒号左边是一个对象,右边是一个函数。该运算符会自动将左边的对象,作为上下文环境(即 this 对象),绑定到右边的函数上面。如果双冒号左边为空,右边是一个对象的方法,则等于将该方法绑定在该对象上面。
注意:该方法不能带参数。
Tips:babel 会将该方法转译成 .bind(this)
的方式。
constructor 方法是类的默认方法,通过new命令生成对象实例时,自动调用该方法。 所以我们可以:
就个人习惯而言,与前两种方法相比,constructor 内绑定在可读性和可维护性上也许有些欠缺。 同时,我们知道在 constructor 声明的方法不会存在实例的原型上,而属于实例本身的方法。每个实例都有同样一个 handleChange,这本身也是一种重复和浪费。
缺点:即使不用到state,也需要添加类构造函数来绑定this,代码量多; 添加参数要在构造函数中bind时指定,不在render中。
注意:组件实例会重复绑定该方法。
推荐:class 类型的组件优先使用该方法,也是性能最好的。
总结一下这种方式的优点:
使用箭头函数,有效绑定了 this;
没有方式一的潜在性能问题;
避免了constructor 内绑定的组件实例重复问题;
A:react 的 app 一般是挂在 body 下面某个div 结点上,如果我想将事件绑定在 body 上(比如监听 body 的滚动事件,window 的 resize 事件)就需要用原生事件。实际上,react 合成事件只是原生 DOM 事件的一个子集,它仅仅实现了 的事件接口,并且统一了浏览器的兼容问题,有些事件 React 并没有实现。
ES next Stage-0 中 提案:
这个方法依赖于 ES next 的新特性,请参考: