React高頻困惑點解疑

爲何要引入React

import React from 'react';

function A() {
  return (
    <h1>魚魚dev</h1>
  )
}

​ 在這裏咱們並無使用到React變量,那爲何還要引用React進來呢?javascript

​ 能夠試一下,若是咱們省略了React的引入操做import React from 'react';,就會報如下的錯誤:報錯信息java

​ 咱們通過babel轉化後,會將上述代碼轉換成:react

function A() {
  return React.createElement("h1", null, "魚魚dev");
}

​ 因此能夠看出來,JSX只是React.createElement(component, props, ...children)的語法糖。es6

爲何要用className而不用class

​ React一開始的理念是想與瀏覽器的DOM API保持一致,而不是與HTML保持一致。因此更願意選擇DOM API中的className屬性。瀏覽器

爲何屬性要用小駝峯

這裏提一句,駝峯命名分爲大駝峯和小駝峯,大駝峯爲首字母爲大寫,其他每一個單詞首字母大寫。小駝峯爲首字母小寫,其他每一個單詞首字母小寫。還有另外兩種主流的命名規則,一是下劃線鏈接符,一是橫槓鏈接符。

​ 同上,React的理念是與瀏覽器的DOM API保持一致,也就使用了DOM API中的小駝峯命名的風格。babel

爲何constructor裏要調用super以及傳入props

​ 這裏有兩個問題dom

  • 爲何constructor裏必需要調用super
  • 爲何super裏必須傳入props

爲何constructor裏必需要調用super

​ 其實這不是React的限制,而是JavaScript的限制。要想在構造函數裏調用this,必須得在此以前調用過super。這裏的緣由涉及到了JavaScript的」繼承「。函數

​ 如若沒有調用super:如若沒有調用super性能

​ JavaScript是經過原型鏈來實現繼承的,在此咱們不深究原型鏈,單說在原型鏈中super的意義。super指代着父類的構造函數,在子類的構造函數中調用super(),則意味着調用了父類的構造函數,從而使得建立子類的實例時,不光有子類的屬性,還會有父類的屬性this

​ 因此說,如過說在子類的構造函數中沒有調用super的話,則子類的實例中不會有父類的屬性值。這就使得這個繼承名存實亡了。因此JavaScript規定繼承時,必須得在子類的構造函數中調用super

爲何super裏必須傳入props

​ 你必須得在super調用時傳入props才能在構造函數中使用this.props,不然使用this.props會報undefined沒有這個屬性。不過在構造函數以後,如render函數中卻能在其中使用this.props

​ 爲何呢?

​ 這是由於在React的構造函數被調用以後,會給建立的React實例對象綁入一個props屬性,其值就是props

const instance = new YourComponent(props);
instance.props = props

​ 不過因爲是在構造函數被調用後,才綁如props屬性,也就是說在構造函數執行時,this是沒有props這個尚需經的。

​ 因此說,仍是得在super中傳入props,不然就沒法在構造函數中使用this.props

class App extends React.Component {
  constructor (props) {
    super(props);// 既調用了super,又傳入了props
    console.log(this.props);// 能夠訪問到值,而且不會報錯
  }
  render () {
    console.log(this.props);// 能夠訪問到值
    return <h1>魚魚dev</h1>
  }
}
class App extends React.Component {
  constructor (props) {
    super();// 調用了super,不傳入props
    console.log(this.props);// undefined
  }
  render () {
    console.log(this.props);// 能夠訪問到值
    return <h1>魚魚dev</h1>
  }
}
class App extends React.Component {
  constructor (props) {
      // 既不調用super,也不傳入props
    console.log(this.props);// 這裏就直接報錯了
  }
  render () {
    console.log(this.props);
    return <h1>魚魚dev</h1>
  }
}

​ 得益於React中的babel的強大,babel提供了es6中都不支持的實例屬性的寫法,也就是說實例屬性你不光能夠在構造函數中聲明,還能夠像這樣寫:

class A {
    a = '1'
}

​ 而若是用這種寫法,也就不用在類中寫構造函數了。因此繼承也不用手動調用super來繼承父類的實例屬性,默認就幫你調用好了。

class A {
  a = '1'
}
class B extends A {
  b = '2'
}
console.log(new B().a);// '1'

​ 因此React中能夠這樣寫:

class App extends React.Component {
  render () {
    console.log(this.props);// 有值
    return <h1>魚魚dev</h1>
  }
}

爲何組件名要用大寫開頭

​ 前面有提到,JSX是React.createComponent(component, props, ...children)的語法糖,在這裏component的類型能夠是stringReactClasstype

  • 當component值爲string類型,react會以爲他是原生dom節點
  • 當component值爲ReactClasstype,react會以爲他是自定義組件

這是在React.createComponent(component, props, ...children),而在JSX中是如何區別是string仍是ReactComponent呢?若是是大寫開頭,是ReactComponent,若是是小寫開頭,是string

function A() {
    // 小寫開頭,React認爲它是原生dom節點。babel後
    // 爲React.createComponent("div",null);
    return <div></div>
}
import MyComponent from './myComponent.js';
function A() {
    // 大寫開頭,React認爲他是自定義組件。babel後
    // 爲React.createComponent(MyComponent,null);
    return <MyComponent></MyComponent>
}
import myComponent from './myComponent.js';// 這裏有個改動,引入自定義組件名小寫開頭
function A() {
    // 小寫開頭,React認爲它是原生dom節點。babel後
    // 爲React.createComponent("myComponent",null);
  // 但因爲dom節點中沒有myComponent,則報錯
    return <myComponent></myComponent>
}

若你自己是想用ReactComponent,但卻一小寫開頭:

若你自己是想用ReactComponent,但卻一小寫開頭

爲何調用方法要 bind this

​ 相信若是你們有研究過JavaScript的this指向問題的話,都知道像下面這種狀況,函數中的this指向會丟失:

const a = {
    func: function () {
        console.log(this);
    }
}
const nextA = a.func;
nextA();// 打印undefined(嚴格模式下)
a.func();// 打印{func: f}

​ 這就是你們常說的this指針丟失的問題。

​ React中的事件綁定也是這樣的:

class Foo extends React.Component {
  handleClick () {
    this.setState({ xxx: aaa })
  }

  render() {
    return (
      <!-- 在這裏我綁定事件未bind(this),結果是當觸發click事件,執行handleClick函數時,內部this指向爲undefined -->
      <button onClick={this.handleClick}>
        Click me
      </button>
    )
  }
}

​ 這是由於onClick={this.handleClick}其實是分爲兩步的:

const handleClick = this.handleClick;
...onClick = handleClick

​ 這就發生了上文所說的this指針丟失。

​ 因此必須得在一開始就肯定死this指向,以保證即便執行了const handleClick = this.handleClick;`...onClick = handleClick`也不會發現this指針丟失的現象。

​ 具體方法有一下兩種:

class Foo extends React.Component {
  handleClick () {
    this.setState({ xxx: aaa })
  }

  render() {
    return (
      // 在綁定的時候再bind(this)。性能不是很好,多處地方調用同一
      // 函數的話,得重複bind
      <button onClick={this.handleClick.bind(this)}>
        Click me
      </button>
    )
  }
}
class Foo extends React.Component {
    constructor () {
    // 在構造函數裏bind(this)。缺點就是寫起來不順手......
    // 沒有人會習慣在類裏聲明瞭函數,再去構造函數裏去bind一次把
        this.handleClick = this.handleClick.bind(this);
    }
  handleClick () {
    this.setState({ xxx: aaa })
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        Click me
      </button>
    )
  }
}

​ 除這兩種方法,還有一種方法:箭頭函數能夠解決這一問題(箭頭函數的this指向徹底繼承於上一做用域的this指向),箭頭函數又有兩種寫法:

class Foo extends React.Component {
    constructor () {
    // 在構造函數裏bind(this)。缺點就是寫起來不順手......
    // 沒有人會習慣在類裏聲明瞭函數,再去構造函數裏去bind一次把
        this.handleClick = this.handleClick.bind(this);
    }
  handleClick () {
    this.setState({ xxx: aaa })
  }

  render() {
    return (
        // 至關於每次點擊事件的時候就聲明一個匿名函數,這個比第一種方法還費性能
      <button onClick={e => {this.handleClick}}>
        Click me
      </button>
    )
  }
}
class Foo extends React.Component {
  // 這是我最喜歡的寫法了,美觀而又省性能
  handleClick = () => {
    this.setState({ xxx: aaa })
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        Click me
      </button>
    )
  }
}
可能你們會想,爲何React不本身bind呢?由於每次調用的時候,都bind一次,會影響性能,倒不如一開始就bind好,而後在調用時候直接調用。
相關文章
相關標籤/搜索