瞭解一個React元件

farsun發表於2021-09-09

最近React,這個專注View層的庫格外火熱,我個人已經探索了一段時間,本來一直想寫一些文字,總結下React應用開發和webpack構建工具的經驗。不過懶癌復發,一直沒動筆。前段時間給團隊安利這個庫,現在要開始用它寫專案了,所以自己挖的坑要填上,就開始寫一些基本的東西,就先從介紹React元件的基礎開始。


從一個簡單的元件開始

標題叫“一個簡單的元件”,於是先來看下官方網站上一個叫做“A Simple Component”的例子來感受下:

var HelloMessage = React.createClass({  render: function() {    return 
Hello {this.props.name}
  } }) React.render(, mountNode)

好的,透過React提供的工廠方法,我們建立了一個元件,在這裡,我們認為createClass建立了一張blueprint,而透過jsx語法建立的則返回一個ReactElement物件來具體地告訴React要如何渲染元件(因為除了blueprint以外還需要一些外部狀態的傳入),這等同於呼叫React.createElement(HelloMessage, { name: 'John' }, 'Hello ', this.props.name)

之後,React就可以根據ReactElement物件,把元件掛載到頁面的某個節點上去,也就是React.render方法在做的事情,這個方法返回一個元件的例項,同時也意味著你可以在已有專案中的一小部分嘗試React。

下面來具體介紹下。

JSX

上面的例子裡看著像是把HTML寫在了Js裡,實際上是透過一種更加清晰易讀且易維護的語法JSX來建立ReactElement物件。對React來說JSX是可選的,如果真的不喜歡也可以用React.createElement這個api。

當然有JSX語法的js檔案是不能直接在瀏覽器中執行的,我們可以用官方的JSXTransformer或者是babel來轉換。

需要注意的是classfor這兩個HTML屬性,由於元件的屬性實際是以物件形式傳遞的,比如上面的{ name: 'John' },另外js不允許關鍵字作為屬性名,所以需要分別用classNamehtmlFor代替。

建立一個元件

建立元件的時候需要給React.createClass提供一個物件,這個物件必須包含一個render方法和若干可選的生命週期方法。

要注意的是我們需要保證render函式是純函式,即同樣的輸入始終返回相同的輸出,並且執行過程中沒有副作用(和DOM互動或者發Ajax請求)。但一個元件要和DOM互動或者發Ajax請求需求是很正常的,那麼就要用到其他生命週期方法了。

除此之外,更重要的部分是,讓一個元件可以工作除了有blueprint外,還需要元件狀態。對於一個React元件來說,分為不可變狀態this.props和可變狀態this.states

我們可以透過this.props決定一個元件內的部分呈現內容,比如上例中我們希望呈現的名字是John,且不會改變。然而對於一個DropDownList而言,僅在點選它時,一個下拉選單才會顯示出來,那麼我們認為這個下拉選單是否顯示就是一個可變狀態。

說到可變狀態,那麼要這麼變?React並不希望我們直接修改this.states,我們需要使用this.setState的方式修改狀態,因為每次呼叫this.setStaterender方法都會被再次呼叫,同時也會呼叫一些相關的生命週期函式。this.setState接受一個物件作為新狀態的patch,也就是說這個物件不會覆蓋現有的this.states,而是一個類似extend的行為。

我們也可以提供一些預設狀態:

getDefaultProps () {  return {    name: 'defaultName'
  };
},getInitialState: function() {  return {    listShowed: false
  };
},

其中getDefaultProps僅會被呼叫一次,這裡的意思是無論你會建立多少個ReactElement,這個函式都只執行一次,之後的預設props都會直接使用改函式的返回值。

這裡需要提一下React提供以ES6的方式建立元件,有意思的是ES6的版本用的是React.Component,在語意上比createClass更加明確。getDefaultPropsgetInitialState在ES6的版本中有些不太一樣,相對與getDefaultProps,ES6將預設屬性物件作為了建構函式的一個屬性,而getInitialState則變成了在其構造器函式中給this.state賦值,來看一個栗子:

class HelloMessage extends React.Component {  constructor (props, context) {    super(props, context)    this.state = {  } // 初始化狀態
  }
  render () {    return 
Hello {this.props.name}
  } } HelloMessage.defaultProps = { name: 'defaultName' }

元件的生命週期

之前的部分一直有提到生命週期函式,下面就來介紹下:

componentWillMount會在元件即將被掛載時呼叫,此時this.refs物件為空物件。如果在該函式中使用this.setState,那麼會更新this.states物件,而render依然只會呼叫一次,相當於是可以覆蓋getInitialState返回的物件,雖然我覺得這沒什麼意義。

componentDidMount是非常常用的生命週期方法,僅當元件被掛載後呼叫一次,這意味著可以在這個函式中進行一些DOM操作等,比如希望元件中的一個textbox可以再掛載後自動獲取焦點:

componentDidMount () {  const textbox = React.findDOMNode(this.refs.text)  if (this.props.autoFocus) textbox.focus()
}

componentWillReceiveProps在將要接受新的props時被呼叫,不是說props是不可變狀態嗎?情況通常是這樣的,當一個父元件包含了一個子元件,子元件的一個props的值是父元件的states的值,那麼當父元件可變狀態改變時,子元件的props也更新了,於是呼叫了這個函式。

componentWillReceiveProps (nextProps) {  if (this.props.disabled !== nextProps.disabled) {    // disabled這個屬性改變了
  }
}

這個生命週期函式componentWillReceiveProps提供了更新states的機會,可以呼叫this.setState,也是唯一可以在元件更新週期中呼叫this.setState的函式。

shouldComponentUpdate是在更新前根據該函式的返回值決定是否進行這次更新。

shouldComponentUpdate (nextProps, nextState) {    // 比較props或者states,返回true則更新照常,返回false則取消更新,且不會呼叫下面的兩個生命週期函式
}

考慮這種情況:父元件有子元件A和子元件B,當父元件呼叫this.setState更新一個作為子元件A屬性的state時,render方法被再次呼叫,此時元件A和元件B同時被更新,其實真正改變的只有元件A,但元件B也同時被要求更新了,這是沒有必要的,於是shouldComponentUpdate就顯的有用了,在該函式體內比較props或是states,如果沒有改變就取消這個更新,這對效能上算是一個提升。

但如果是複雜物件的比較就比較麻煩了,因為我們無法透過===來判斷兩個物件的鍵值是否都相等,於是我們就希望我們的物件是不可變的(immutable)。這裡不再展開了,大家可以先自行探索,之後再寫這個部分的文字,並介紹immutable.js

componentWillUpdate在元件被更新前呼叫一次,可以用來做一些更新前的準備工作,舉個例子:

componentWillUpdate (nextProps, nextState) {  if (!this.props.isShowed && nextProps.isShowed) {    // 比如下拉選單此時變的顯示了,可以對此監聽一些事件什麼的
  }  if (this.props.isShowed && !nextProps.isShowed) {    // 比如下拉選單隱藏了,可以在這裡取消事件監聽
  }
}

(元件更新前事情還蠻多的...)

componentDidUpdate在元件更新完成後呼叫,可以考慮在這個函式中執行一些DOM操作什麼的。

注意:絕對不要在componentWillUpdatecomponentDidUpdate中呼叫this.setState方法,否則將導致無限迴圈呼叫。

componentWillUnmount會在元件即將從掛載點移去時呼叫,此方法專門用來『擦屁股』,比如去除即將被銷燬的DOM節點的引用,或者是清除計時器,取消監聽的時間等等。


好了,正文結束,這一篇介紹了React比較基礎的部分,希望對大家有幫助,之後還會有一些更有針對性的部分。如文章中內容有誤,還請指正。



作者:leozdgao
連結:


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4289/viewspace-2802727/,如需轉載,請註明出處,否則將追究法律責任。

相關文章