事件绑定

React 事件绑定大概有如下几种,先给最终源码再逐步解析。javascript

bind 方式java

 1 class Profile extends React.Component<{ name: string, age: number }, { linkd: number }> {
 2     constructor(props: any) {
 3         super(props);
 4         this.state = {
 5             linkd: 0
 6         }
 7         this.onLiked = this.onLiked.bind(this);
 8     }
 9     render() {
10         return (
11             <div >
12                 <h2 onClick={this.onLiked} >给我点赞</h2>
13                 <h1>总点赞数 {this.state.linkd}</h1>
14             </div >
15         );
16     }
17     onLiked() {
18 
19         let liked = this.state.linkd;
20 
21         liked++;
22 
23         this.setState({
24             linkd: liked
25         });
26     }
27 }
TypeScript 版源码

第一步:定义事件处理方法react

onLiked() {

        let liked = this.state.linkd;

        liked++;

        this.setState({
            linkd: liked
        });
    }

第二步:为h2标签绑定点击事件并指定回掉app

<h2 onClick={this.onLiked} >给我点赞</h2>

第三布:在构造函数中为 onLiked 方法指定上下文对象ide

this.onLiked = this.onLiked.bind(this);

解析:函数

  这是最为常规的处理方式,咱们经过调用 bind  方法。性能

  第一步,为当前实例 Profile 组件建立自有属性 onLiked 方法。也就是将原型链的 onLiked 指针给了一份给当前自有属性,bind() 函数会建立一个新函数(称为绑定函数)为 onLiked 方法指定当前上下文对象,保证 onLiked 函数中 this 不会丢失。理论上来讲咱们定义事件处理函数是做为 Profile 组件的原型链上所以 onLiked 方法中 this 的指向应该是 Profile 组件的实例,而且在 TypeScript 的静态类型检测中也不会有编译问题,可是咱们运行发现若是仅作到前两步会报类型错误this

Uncaught TypeError: Cannot read property 'state' of undefined

  当咱们查看 React 源码最终触发事件的方法时会发现spa

var invokeGuardedCallback = function (name, func, context, a, b, c, d, e, f) {
  this._hasCaughtError = false;
  this._caughtError = null;
  var funcArgs = Array.prototype.slice.call(arguments, 3);
  try {
    func.apply(context, funcArgs);
  } catch (error) {
    this._caughtError = error;
    this._hasCaughtError = true;
  }
};

  咱们定义的事件处理函数 func 的指针被指向 context 对象,那么 context 对象是什么呢,经过找到 context 来源prototype

function executeDispatch(event, simulated, listener, inst) {
  var type = event.type || 'unknown-event';
  event.currentTarget = getNodeFromInstance(inst);
  ReactErrorUtils.invokeGuardedCallbackAndCatchFirstError(type, listener, undefined, event);
  event.currentTarget = null;
}
  这里为 context 对象指定为 undefined 也就是说事件回掉函数并非以 Profile 组件的实例对象来触发的。知道这个概念想要保持回掉方法中 this 的指针为 Profile 对象则会有不少方法。

优势:

  仅会给 onLiked 指定一次

  render 方法渲染中,不会由于回掉处理函数指向的改变而进行多余的渲染

缺点:

  每次定义回掉函数都须要在构造函数中指定指针

 

JSX 内嵌箭头函数方式

 1 class Profile extends React.Component<{ name: string, age: number }, { linkd: number }> {
 2     constructor(props: any) {
 3         super(props);
 4         this.state = {
 5             linkd: 0
 6         }
 7     }
 8     render() {
 9         return (
10             <div >
11                 <h2 onClick={() => { this.onLiked() }} >给我点赞</h2>
12                 <h1>总点赞数 {this.state.linkd}</h1>
13             </div >
14         );
15     }
16     onLiked() {
17 
18         let liked = this.state.linkd;
19 
20         liked++;
21 
22         this.setState({
23             linkd: liked
24         });
25     }
26 }
TypeScript 版源码

第一步:与 bind 方式同样定义事件处理方法

onLiked() {

        let liked = this.state.linkd;

        liked++;

        this.setState({
            linkd: liked
        });
    }

第二步:为 h2 标签绑定点击事件并指定回掉,不过这里咱们在调用以前再嵌套一层箭头函数且回掉方法带上()执行语法

render() {
        return (
            <div >
                <h2 onClick={() => { this.onLiked() }} >给我点赞</h2>
                <h1>总点赞数 {this.state.linkd}</h1>
            </div >
        );
    }

对比一下最终生成 javascript 代码

Profile.prototype.render = function () {
        var _this = this;
        return (React.createElement("div", null,
            React.createElement("h2", { onClick: function () { _this.onLiked(); } }, "\u7ED9\u6211\u70B9\u8D5E"),
            React.createElement("h1", null,
                "\u603B\u70B9\u8D5E\u6570 ",
                this.state.linkd)));
    };

解析:

  相比于 bind 方式,嵌套箭头函数避免了在构造函数中指定指针步骤,最终 React 调用回掉时的空指向这里不会有影响,因为变量函数做用域特性,每次 render 执行时都会为保存当前 Profile 对象指针,以此来保证 this 正确指向。可是缺点也很明显,React 在将虚拟的 DOM 对应生成真实 DOM 节点以前, React 会将虚拟的 DOM 树与先前的进行比较(Diff),计算出变化部分,再将变化部分做用到真实 DOM 上,实现最终界面的更新。方法在每次调用时,方法在定义时会保存一个做用域链,在方法每次调用时会建立一个全新的对象将该方法内局部变量做为属性保存到新对象并把新对象保存到最初做用域链上。所以每次 render 方法调用时都建立了一个局部变量来保存 this 指针。对于虚拟DOM来讲,每次都是新的变量所以会进行没必要要的渲染。

 

优势:

   不须要每次在构造函数中指定

缺点:

  不必的性能损耗

 

做为属性的箭头函数方法

class Profile extends React.Component<{ name: string, age: number }, { linkd: number }> {
    constructor(props: any) {
        super(props);
        this.state = {
            linkd: 0
        }
    }
    render() {
        return (
            <div >
                <h2 onClick={this.onLiked} >给我点赞</h2>
                <h1>总点赞数 {this.state.linkd}</h1>
            </div >
        );
    }
    onLiked = () => {

        let liked = this.state.linkd;

        liked++;

        this.setState({
            linkd: liked
        });
    }
}
TypeScript 版源码

第一步:定义事件处理方法

onLiked = () => {

        let liked = this.state.linkd;

        liked++;

        this.setState({
            linkd: liked
        });
    }

第二步:为 h2 标签绑定点击事件并指定回掉

render() {
        return (
            <div >
                <h2 onClick={this.onLiked} >给我点赞</h2>
                <h1>总点赞数 {this.state.linkd}</h1>
            </div >
        );
    }

解析:

  做为给予 TypeScript 或者 Babel 开发环境来讲。这最简介且集合 bind 方式不会重复渲染等优势,同时也没必要像 bind 同样每次去定义

优势:

  简洁,同时保证执行效率额

缺点:

   须要经过编译器或支持 ES6 的运行环境

 

注意:常规事件回掉处理中 context 指向对象为 undefined 可是因为 react 源码调用错综复杂,也不能排除在特殊场景中会为回掉方法指定非空的上下文对象。可是目前尚未碰到这种场景。

相关文章
相关标签/搜索