1. 程式人生 > >每日質量NPM包事件繫結_bindme(詳解React的this)

每日質量NPM包事件繫結_bindme(詳解React的this)

一、bindme

官方定義: is a helper to bind a list of methods to an object reference

理解: 因為不推薦在render()裡構建函式,作者就用了6行程式碼封裝了函式繫結事件的程式碼. bindme的npm包實際上由6行ES5程式碼組成,但是確實方便了很多.這個包值得一用

二、用法

代替箭頭函式和多層bind

有時候我們並不會直接在建立事件的時候bind,而是統一在constructor上繫結事件,如果一個模組大起來,就會出現好幾十行繫結事件的程式碼,用bindme可以很好解決.例如

//原寫法
this.clickEvent1 = this.clickEvent1.bind(this)
this.clickEvent2 = this.clickEvent2.bind(this)
this.clickEvent3 = this.clickEvent3.bind(this)
this.clickEvent4 = this.clickEvent4.bind(this)
this.clickEvent5 = this.clickEvent5.bind(this)

//bindme寫法
bindme(this, 'clickEvent1', 'clickEvent2', 'clickEvent3', 'clickEvent4', 'clickEvent5')

又好看又方便

也可以在super構建例項的時候繫結

bindme(super(props),
    'clickEvent1',
    'clickEvent2',
    'clickEvent3',
    'clickEvent4',
    'clickEvent5',
)

就是這麼簡單的用法, 我們可以看看它的原始碼轉換成ES6是怎麼樣的

const bindme = (self, ...funcs) => {
  funcs.forEach(func => {
    if (self[func]) {
      self[func] = self[func].bind(self)
    } else {
      console.error(`Method ${func} is not defined`)
    }
  })
}

其實也就是收集所有的事件,再統一bind,如果不存在的丟擲異常.我們在平時也會經常封裝一些類似這些小的便捷操作

結合React

import React,{ PureComponent } from 'react'

import bindme from 'bindme'
import './style.css'
export default class BindmeComp extends PureComponent{
    constructor(props){
        bindme(super(props),
            'bindmeOnMouseOver'
        )

        this.initClickMode = this.initClickMode.bind(this)
        
        bindme(this, 'bindmeFirClickMode', 'bindmeSecClickMode')
    }

    noThisClickMode(){
        console.log('未繫結this事件 =========>', this)
    }

    initClickMode(){
        console.log('普通bind事件 ===========>', this)
    }

    arrowClickMode = () => {
        console.log('箭頭函式bind事件 ===========>', this)
    }

    bindmeFirClickMode(){
        console.log('bindme事件1 ===========>', this)
    }

    bindmeSecClickMode(){
        console.log('bindme事件2 ===========>', this)
    }

    bindmeOnMouseOver(){
        console.log('bindme事件3 ===========>', this)
    }

    render(){
        return(
            <div>
                <div className="list">
                    <span>未繫結this事件</span>
                    <button onClick={ this.noThisClickMode }>點選</button>
                </div>
                <div className="list">
                    <span>普通bind事件</span>
                    <button onClick={ this.initClickMode }>點選</button>
                </div>
                <div className="list">
                    <span>箭頭函式事件</span>
                    <button onClick={ this.arrowClickMode}>點選</button>
                </div>
                <div className="list">
                    <span>bindme</span>
                    <button onClick={ this.bindmeFirClickMode }>點選</button>
                </div>
                <div className="list">
                    <span>bindme2</span>
                    <button onClick={ this.bindmeSecClickMode } >點選</button>
                </div>

                <div className="list">
                    <span>bindme3</span>
                    <button onMouseOver={ this.bindmeOnMouseOver } >滑過</button>
                </div>
            </div>
        )
    }
}

上面基本覆蓋了常用的事件繫結情況.我們看看都會輸出什麼?

可以看到,除了第一個this為undefined外,其他都是正常繫結在了元件上

擴充套件一下React的this

我們先從我們最熟悉js的this說起;

面試官經常會問,什麼是this?this指向什麼? 一般來說: 誰呼叫該function,該函式的this就指向這個呼叫方

有一個特殊的情況,ES6的箭頭函式,也是面試官特別喜歡的一個東西(但它確實好用),由於箭頭函式不存在this,所以它的this是由上一層繼承而來的.所以可以得出結論箭頭函式的this始終指向構造時的物件,而不是使用時的物件

另外箭頭函式還有一些不同於普通函式的特性,比如: 不可以當作建構函式使用,即不可以new. 不可以使用arguments屬性等等

總結一句話就是: 普通函式this是動態的,而箭頭函式的this是靜態的

我們細說一下React的this.

因為React元件是通過class構造的,所以元件裡的所有屬性,所有方法都可以通過this來獲取,例如我們經常使用的屬性state.就可以通過this.state.xxx獲取

所以我們只需要保證this永遠指向該構造物件(元件)即可,所以我們一般不會通過function來建立函式(會使得this動態指向呼叫function的物件), 並且:

React元件類的方法沒有預設繫結this到元件例項,需要手動繫結。

所以就衍生出了繫結this的方法;常用的有四種,我們一般會使用在constructor裡面bind

//優先順序最高
constructor(props){
    super(props);
    
    this.handleEvent = this.handleEvent.bind(this)
}

這種和直接在構建事件中繫結很像對吧?

<input onChange={this.handleEvent.bind(this)} />

原理雖然是一樣的,但是效能卻差了一大截.為什麼?

我們知道只要state值改變了,就會導致render重新渲染,如果直接在建立事件繫結,那每一次渲染的時候都需要重新繫結一次,會大大降低效能.相反,只要在構造的時候繫結,無論渲染多少次,繫結都還是一次

此外還有比較常用的箭頭函式繫結法和::繫結法 前面也介紹了箭頭函式this的知識,所以在React出現也是蠻高的

handleEvent = () => {
    ...
}

<input onChange={this.handleEvent} />

::雖然也可以繫結this,但是因為不能傳引數,所以不經常使用

handleEvent = () => {
    ...
}

<input onChange={::this.handleEvent} />