個人React之路--入門

React的學習之路還要繼續走下去,最近一邊在作未完成的項目一邊學習React,項目是vue寫的,後面還須要有一個後臺管理系統計劃使用react完成,寒假說長也不長,要抓緊時間了。javascript

有人愛有人恨的語法糖--jsx

jsx簡介

不少人不喜歡React,很大程度上是由於不喜歡jsx,那麼jsx究竟是什麼呢?首先仍是不要忘了React的基本哲學--一切都是js,包括文檔結構。因此曾經天天都會見到的html在react的世界裏全都不存在,react經過一系列叫作react元素的對象來構建虛擬DOM結構,最原始的建立react元素的方式是這樣的:css

const root = React.createElement('div', { className: 'main' }, '我是一個div');

最終它將返回一個大概這樣子的對象(有所簡化過,不表明在 React 源碼中是這樣):html

const root = {
  type: 'div',
  props: {
    className: 'main',
    children: '我是一個div'
  }
};

這樣一個個建立節點實際上是很麻煩的,想一想看,若是每一個整個虛擬DOM的內容都要經過React.createElement來建立,代碼量會不少,並且咱們根本沒法直觀地看出樹形結構,不管開發仍是維護性都及其不友好。爲了解決這一問題,一種新型的,相似xml結構的語法擴展就誕生了,就是jsx。vue

上面的代碼結構改爲jsx的書寫方式就是這樣的:java

const root =(
  <div className="main">
      我是一個div
  </div>
);

這個結構就很熟悉了,不過要記住,他不是html模板,它就是js,最終在執行以前會被徹底轉義成爲純js代碼,因此使用jsx是不存在任何性能問題的。react

jsx語法

jsx的標準語法結構和xml徹底相似,特別的,jsx中的html屬性要寫成小駝峯命名的形式,好比onclick就要寫成onClick。另外一點須要注意的問題就是,因爲class是js中的保留字,因此要用className來代替。算法

jsx使用jsx時候要時刻記住它是js表達式,因此它能夠像普通的js表達式同樣,賦值傳參返回均可以。而在jsx內部若是想使用表達式,就須要放在{}裏面。這就是jsx語法,很是簡單,也不須要記憶特殊的指令,一切均可以和處理js同樣來處理,下面來看一個小例子:redux

const item = this.newsList.map((news, index) => (
    <li key={index}>
      <span>{news.title}</span>
      <span>{news.desc}</span>
      {news.image ? <img src="{news.image}"/> : null}
    </li>
  )
);
const list = (
  <ul>
    {item}
  </ul>
);

這是一個在react開發中特別常見的使用場景,咱們獲得了一個數組的數據,須要以列表的形式渲染出來。在react中不須要使用任何迭代判斷的相關指令語法,只要會寫js的都能看懂上面的邏輯:經過數組的map方法迭代數組的內容,在回調函數中處理數據,渲染成想要的樣式,就獲得了一條一條列表項,插到列表裏面就完成數據渲染了。咱們能夠發如今不管是迭代方法仍是三元表達式等等,只要是js語法就能夠隨意地寫到裏面,自由度很是高。數組

可複用的基本單位--組件

爲何要使用組件

瞭解了jsx,下一個重要概念就是組件了。組件不是react特有的,組件化開發有不少好處,組件符合高內聚低耦合的要求,每個組件是封裝了視圖和邏輯的一個相對獨立的個體,而整個頁面是由多個組件構成的,每一個組件能夠屢次複用。babel

組件能夠理解爲相似於函數調用同樣,定義好的組件是一個抽象的視圖,而咱們經過傳入相關的「參數」來使它展現出咱們想要的樣子,組件就是咱們複用各類獨立部件的基本單位。

React中的組件

定義一個組件最簡單的方式是使用JavaScript函數:

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

這是一個最簡單的函數定義組件,整個函數調用結果實際上就是返回一個<h1>標籤,不過特別的是,標籤的內容不是肯定的,它是由咱們傳入的參數來決定的。這就是組件開發,在React中,最經常使用的不是函數聲明組件,而是向下面同樣:

class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

此處使用了ES6的類和繼承,建立了一個繼承自Component的類。這個組件和上一個組件效果是徹底相同的,下面來重點分析React組件中幾個重要的概念。

核心數據狀態--state

什麼是state?狀態,在react組件中,state是指一個組件UI呈現的最小狀態集。在react中,視圖層的更新是經過處理狀態的變化來實現的,而state就是對這一系列狀態的定義。react的數據是單向流動的,數據只能從模型層流向視圖層,對應到具體的實現,咱們對state所作的一系列處理會自動的反映到視圖上,咱們想要更新視圖,只更新狀態便可。說的可能比較抽象,看一個具體例子:

class ClickMe extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }
  clicked() {
    this.setState({
        count: this.state.count + 1
    });
  }
  render() {
    return (
      <div onClick={() => this.clicked()}>
        點我{this.state.count}次
      </div>
    );
  }
}

這個組件的效果就是點擊文字,會顯示你的點擊次數,效果很簡單就不截圖了,關於組件生命週期和點擊事件綁定後面再看,這裏重點來看state的變化。在構造函數裏面初始化state的數據,把state數據放在頁面上,點擊時候調用setState方法改變state中的數據。

關於state有幾點須要注意:

  1. state不能直接修改,直接修改state的值是不會更新視圖的,正確的更新方式是使用setState來改變state的值。
  2. 不是全部的變量都要放到state中,state裏面的變量一方面是要來描述組件自身狀態,不須要反映到視圖上的內容不是state。
  3. state是最小狀態集,取自父組件的狀態信息不是自身狀態,不能放在state中。從外部傳入的東西要放在props中。

外部傳遞屬性--props

props是組件的另外一個很是重要的概念,props指的是從外部傳入的屬性。props是React中父組件向子組件通訊的方式,下面是一個簡單的例子:

class Child extends React.Component {
  constructor(props) {
    super(props);
  }
  render() {
    return (
      <div>
        {this.props.data}
      </div>
    );
  }
}

使用組件

<Child data="我是顯示的數據"></Child>

咱們定義組件時候在構造函數中能夠接收到props參數,而且要使用super傳到Component的構造方法中。在整個組件的類中就可使用成員變量props了。而props的內容,是父元素在調用子元素時候以屬性的形式傳入的。整個props控制的就是從父元素到子元素的事件流,這樣咱們在使用組件的時候就能夠像函數調用同樣使用組件,像傳入參數同樣傳入props。

使用props時候要注意一點,props傳遞數據是單向的,數據只能從父組件傳遞到子組件,須要其它方向的數據傳遞就須要使用其餘方式了。

組件從建立到銷燬--生命週期

生命週期這個概念在不少開發中都會接觸,react也是如此,一個react組件從建立運行到銷燬須要經歷不少階段,系統也爲咱們提供了對應階段的hook方法(hook方法翻譯爲鉤子方法,指的是當組件運行到對應的階段時候就會自動執行寫在這些方法裏面的邏輯),我從網上找到了一副描述比較清晰的圖片(侵刪):

下面來逐一介紹這些生命週期方法以及它們說發揮的做用

  1. getDefaultProps和getInitialState,若是使用ES6的類繼承方式定義組件是看不到這兩個方法的,它們的任務是組件加載前先獲取默認props和初始化state,在ES6的語法中咱們能夠在constructor中對其進行定義,注意constructor第一句必需要使用super(props),不然會報錯。
  2. componentWillMount,在組件渲染以前調用,整個生命週期只會調用一次,子組件的該方法會在父組件調用以後被調用,若是在該方法內設置狀態,react會在狀態設置好以後才執行渲染,經常使用在該方法裏發送網絡請求獲取數據。
  3. render(),組件渲染方法,此方法返回組件最終被渲染的狀態,它的做用就是渲染組件,此階段不能修改state。從圖上能夠看出,除了首次渲染要調用,此方法在組件發生更新時候也會被調用,它是組件最核心的方法。
  4. componentDidMount,在逐漸被渲染以後被調用,僅調用一次,子組件的此方法會在父組件的此方法以前調用,此方法結束後組件進入運行狀態。
  5. componentWillReceiveProps(nextProps),組件運行階段,當組件接收到新的props時被調用,這個函數接收一個object參數(新的props),父組件發生render的時候子組件就會調用,組件首次渲染不會觸發。
  6. shouldComponentUpdate(nextProps, nextState),組件運行階段,接收到新的state或props時被調用,此方法默認返回true,能夠經過控制該方法返回false來阻止組件從新渲染。
  7. componentWillUpdate,組件運行階段,當準備從新渲染組件前調用,作一些渲染前準備工做,組件首次渲染不會觸發。
  8. componentDidUpdate,組件運行狀態,組件從新渲染以後調用,組件首次渲染不會觸發。
  9. componentWillUnmount,在組件被卸載前調用,作一些結束前的清理工做。

以上是react生命週期的相關內容,到此,react組件的基本概念就介紹的差很少了。

行爲與交互--事件綁定

在react中綁定事件須要注意一個問題,若是是使用ES6的class方式定義的組件中事件處理函數的this默認是不會綁定的,咱們須要手動綁定this指向。來看下面一個錯誤的例子:

class EventTest extends React.Component {
  clicked() {
    console.log('clicked');
  }
  render() {
    return (
      <div onClick={this.clicked}>
        點我
      </div>
    );
  }
}

點擊點我,確實可以正常打印出clicked,看起來好像沒有問題,可是,若是試着打印一下this,就會發現結果是undefined。

這樣寫this沒辦法綁定,天然也就沒辦法使用各類成員變量和方法,也不能調用內置方法了,顯然不是咱們預期的,因此咱們須要手動來綁定this指向,方法也很簡單:

class EventTest extends React.Component {
  clicked() {
    console.log('clicked');
  }
  render() {
    return (
      <div onClick={this.clicked.bind(this)}>
        點我
      </div>
    );
  }
}

只要增長bind(this)就能實現預期效果了,這也是一種經常使用的綁定this方式。除此以外還能夠採用箭頭函數來自動綁定this,下面的作法也是徹底能夠的:

class EventTest extends React.Component {
  clicked() {
    console.log('clicked');
  }
  render() {
    return (
      <div onClick={()=>this.clicked()}>
        點我
      </div>
    );
  }
}

把clicked做爲箭頭函數返回的函數來使用,利用箭頭函數內部自動綁定this的特性也能夠實現this綁定。另外,還有一種寫法:

class EventTest extends React.Component {
  clicked = () => {
    console.log('clicked');
  }
  render() {
    return (
      <div onClick={this.clicked}>
        點我
      </div>
    );
  }
}

這種方法是新的ES標準中的實驗性語法,因爲有babel轉譯也是可使用的,官網上面也提到了這種寫法,不過因爲新的標準還未成熟,因此用的人也很少。


瞭解了這些,react算是入了門了,接下來深刻學習的路還長,虛擬DOM的原理,diff算法,css-in-js,工程化下的react項目結構,react-router,redux,還有之後要學習的react native,後面隨着學習慢慢總結。

相關文章
相關標籤/搜索