一、React 事件处理
React 元素的事件处理和 DOM 元素类似。但是有一点语法上的不同:
1、事件绑定属性写法
React 事件绑定属性的命名采用驼峰式写法,而不是小写。
如果采用 JSX 的语法你需要传入一个函数作为事件处理函数,而不是一个字符串(DOM 元素的写法)
<button onClick={activateLasers}>
激活按钮
</button>
2、阻止默认事件
在 React 中另一个不同是你不能使用返回 false 的方式阻止默认行为, 你必须明确的使用 preventDefault。
function ActionLink() {
function handleClick(e) {
e.preventDefault();
console.log('链接被点击');
}
return (
<a href="#" onClick={handleClick}>
点我
</a>
);
}
3、合成事件的实现机制
在React底层,对合成事件做了两件事:事件委派 和 自动绑定
3.1 事件委派
React 不会把事件处理函数直接绑定到真实的节点上,而是把所有的事件绑定到结构的最外层,使用一个统一的事件监听器,这个事件监听器维持一个映射来保存所有组件内部的事件监听和处理函数。
每当组件挂载和卸载时,只是在这个统一的事件监听器上插入或删除一些对象。
当事件发生时,首先被这个统一的事件监听器处理,然后在映射里真正的事件处理函数并调用。
这样做简化了事件处理和回收机制,效率大大的提升了
3.2 自动绑定
React组件中,每个方法的上下文会指向该组件的实例,即自动绑定this为当前组件。而且React还会对这种引用进行缓存,达到CPU和内存的最优化。
但是:
ES6 classes 或者 纯函数,这种自动绑定不存在,需要手动绑定this,有三种方法进行手动绑定:
3.2.1、bind方法
const tabs = [
{ title: '今日推荐', sub: '1' },
{ title: '福利', sub: '2' },
{ title: '热点', sub: '3' },
{ title: '信用卡', sub: '4`' },
];
class TheTab extends Component {
handleClick (e) {
this.props.onClick(e.sub)
}
render() {
return (
<div>
<Tabs tabs={tabs}
initialPage={0}
onChange={this.handleClick.bind(this)}
>
</Tabs>
<WhiteSpace />
</div>
)
}
}
3.2.2、构造器内声明
好处在于仅需要进行一次绑定,而不需要再次调用事件监听器时去执行绑定事件。
class TheTab extends Component {
constructor(props) {
super(props)
this.handleClick = this.handleClick.bind(this)
}
handleClick (e) {
this.props.onClick(e.sub)
}
render() {
return (
<div>
<Tabs tabs={tabs}
initialPage={0}
onChange={this.handleClick}
>
</Tabs>
<WhiteSpace />
</div>
)
}
}
3.2.3、箭头函数
箭头函数自动绑定了此函数作用域的this,因此不需要再对他使用bind方法,使用起来也很方便。
class TheTab extends Component {
const handleClick = (e) => {
this.props.onClick(e.sub)
}
render() {
return (
<div>
<Tabs tabs={tabs}
initialPage={0}
onChange={this.handleClick}
>
</Tabs>
<WhiteSpace />
</div>
)
}
}
4、向事件处理程序传递参数
通过 bind 方式向监听函数传参,在类组件中定义的监听函数,事件对象 e 要排在所传递参数的后面,例如:
class Popper extends React.Component{
constructor(){
super();
this.state = {name:'Hello world!'};
}
preventPop(name, e){ //事件对象e要放在最后
e.preventDefault();
alert(name);
}
render(){
return (
<div>
<p>hello</p>
{/* 通过 bind() 方法传递参数。 */}
<a
href="https://reactjs.org"
onClick={this.preventPop.bind(this,this.state.name)}
>Click</a>
</div>
);
}
}
二、组件性能优化
影响网页性能最大的因素是浏览器的重绘(reflow)和重排版(repaint)。React 的Virtual Dom就是尽可能的减少重绘和重排版。
从React的渲染过程来看,如何防止不必要的渲染,这里提供了一个便捷的方法就是PureRender。
1、纯函数
纯函数由三大原则构成:
- 给定相同的输入,总是返回相同的输出
- 过程没有副作用,即不改变外部状态
- 没有额外的状态依赖
纯函数是函数式编程的基础,它完全独立于外部状态,这样避免了因共享外部状态而导致的bug。
React在设计时带有函数式编程的基因,因为React组件本身就是纯函数。React的creatElement方法保证了组件是纯净的,即传入相同的props得到一定的Virtual Dom,整个过程都是可预测的。
2、PureRender
PureRender中的Pure就是组件满足纯函数的条件。
2.1 PureRender的本质
在生命周期方法shouldComponentUpdate中,让当前传入的props和state和之前的做浅比较,如果返回false,组件不会执行render方法。
理想情况下,要做到充分比较,需要深比较,但是代价太昂贵了。
PureRender对Object只做了引用比较,并没有做值比较,这是一个取舍问题,PureRender源代码中只对新旧props作了浅比较。
2.2 运用PureRender
在构建组件时,可以使用官方提供的插件,‘react-addons-pure-render-mixin’
import React, {Component} from 'react';
import { Tabs, WhiteSpace, Badge } from 'antd-mobile';
import PureRenderMixin from 'react-addons-pure-render'
class TheTab extends Component {
constructor(props) {
super(props)
this.shouldComponentUpdate =
PureRenderMixin.shouldComponentUpdate.bind(this)
}
render() {
return (
<div>
<Tabs tabs={tabs} initialPage={0}></Tabs>
</div>
)
}
}
在组件开发过程中要尽量地满足Pure,这样才能保证对相应的变更作出最少的渲染。
2.3 优化PureRender
2.3.1 避免直接为props设置对象和数组
下面的代码中,每次渲染时style都是新对象,都会导致重新渲染
<Account style={{color: 'black'}} />
我们需要把这一类的赋值操作改为 提前赋值成常量。
<Account style={this.props.style} />
还有一种情况时默认值也是一样的道理:
<Account style={this.props.style || {}} />
我们为默认值也设置为一个常量
const defaultStyle = {}
<Account style={this.props.style || defaultStyle} />
2.3.2 设置props方法优化绑定事件
我们不用每次都绑定事件,因此把绑定事件移到构造器内。
class TheTab extends Component {
constructor(props) {
super(props)
this.handleClick = this.handleClick.bind(this)
}
handleClick (e) {
this.props.onClick(e.sub)
}
render() {
return (
<div>
<Tabs tabs={tabs}
initialPage={0}
onChange={this.handleClick}
>
</Tabs>
</div>
)
}
}
2.3.2 优化子组件设置
设置了子组件的组件在调用shouldComponentUpdate时,均返回true,因此要给子组件设置PureRender,也就是提到父组件来判断。
import PureRenderMixin from 'react-addons-pure-render'
class TheTab extends Component {
constructor(props) {
super(props)
this.handleClick = this.handleClick.bind(this)
this.shouldComponentUpdate =
PureRenderMixin.shouldComponentUpdate.bind(this)
}
handleClick (e) {
this.props.onClick(e.sub)
}
render() {
return (
<div>
<Tabs tabs={tabs}
initialPage={0}
onChange={this.handleClick}
>
</Tabs>
</div>
)
}
}