事件綁定

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 源碼調用錯綜複雜,也不能排除在特殊場景中會爲回掉方法指定非空的上下文對象。可是目前尚未碰到這種場景。

相關文章
相關標籤/搜索