React 入門執行個體教程

來源:互聯網
上載者:User

標籤:

React 入門執行個體教程 

 阮一峰

日期: 2015年3月31日

現在最熱門的前端架構,毫無疑問是 React 。

上周,基於 React 的 React Native 發布,結果一天之內,就獲得了 5000 顆星,受矚目程度可見一斑。

React 起源於 Facebook 的內部項目,因為該公司對市場上所有 JavaScript MVC 架構,都不滿意,就決定自己寫一套,用來架設 Instagram 的網站。做出來以後,發現這套東西很好用,就在2013年5月開源了。

由於 React 的設計思想極其獨特,屬於革命性創新,效能出眾,代碼邏輯卻非常簡單。所以,越來越多的人開始關注和使用,認為它可能是將來 Web 開發的主流工具。

這個項目本身也越滾越大,從最早的UI引擎變成了一整套前後端通吃的 Web App 解決方案。衍生的 React Native 項目,目標更是宏偉,希望用寫 Web App 的方式去寫 Native App。如果能夠實現,整個互連網行業都會被顛覆,因為同一組人只需要寫一次 UI ,就能同時運行在伺服器、瀏覽器和手機(參見《也許,DOM 不是答案》)。

既然 React 這麼熱門,看上去充滿希望,當然應該好好學一下。從技術角度,可以滿足好奇心,提高技術水平;從職業角度,有利於求職和晉陞,有利於參與潛力大的項目。但是,好的 React 教程卻不容易找到,這一方面因為這項技術太新,剛剛開始走紅,大家都沒有經驗,還在摸索之中;另一方面因為 React 本身還在不斷變動,API 一直在調整,至今沒發布1.0版。

我學習 React 時,就很苦惱。有的教程討論一些細節問題,對入門沒協助;有的教程寫得不錯,但比較短,無助於看清全貌。我斷斷續續學了幾個月,看過二十幾篇教程,在這個過程中,將對自己有協助的 Demo 都收集下來,做成了一個庫 React Demos 。

下面,我就根據這個庫,寫一篇全面又易懂的 React 入門教程。你只需要跟著每一個 Demo 做一遍,就能初步掌握 React 。當然,前提是你必須擁有基本 JavaScript 和 DOM 知識,但是你讀完就會發現,React 所要求的預備知識真的很少。

零、安裝

React 的安裝包,可以到官網下載。不過,React Demos 已經內建 React 源碼,不用另外安裝,只需把這個庫拷貝到你的硬碟就行了。

$ git clone [email protected]:ruanyf/react-demos.git

如果你沒安裝 git, 那就直接下載 zip 壓縮包。

下面要講解的12個例子在各個 Demo 子目錄,每個目錄都有一個 index.html 檔案,在瀏覽器開啟這個檔案(大多數情況下雙擊即可),就能立刻看到效果。

需要說明的是,React 可以在瀏覽器運行,也可以在伺服器運行,但是本教程只涉及瀏覽器。一方面是為了盡量保持簡單,另一方面 React 的文法是一致的,伺服器的用法與瀏覽器差別不大。Demo13 是伺服器首屏渲染的例子,有興趣的朋友可以自己去看源碼。

一、HTML 範本

使用 React 的網頁源碼,結構大致如下。

<!DOCTYPE html><html>  <head>    <script src="../build/react.js"></script>    <script src="../build/react-dom.js"></script>    <script src="../build/browser.min.js"></script>  </head>  <body>    <div id="example"></div>    <script type="text/babel">      // ** Our code goes here! **    </script>  </body></html>

上面代碼有兩個地方需要注意。首先,最後一個 script 標籤的 type 屬性為 text/babel 。這是因為 React 專屬的 JSX 文法,跟 JavaScript 不相容。凡是使用 JSX 的地方,都要加上 type="text/babel" 。

其次,上面代碼一共用了三個庫: react.js 、react-dom.js 和 Browser.js ,它們必須首先載入。其中,react.js 是 React 的核心庫,react-dom.js 是提供與 DOM 相關的功能,Browser.js 的作用是將 JSX 文法轉為 JavaScript 文法,這一步很消耗時間,實際上線的時候,應該將它放到伺服器完成。

$ babel src --out-dir build

上面命令可以將 src 子目錄的 js 檔案進行文法轉換,轉碼後的檔案全部放在 build 子目錄。

二、ReactDOM.render()

ReactDOM.render 是 React 的最基本方法,用於將模板轉為 HTML 語言,並插入指定的 DOM 節點。

ReactDOM.render(  <h1>Hello, world!</h1>,  document.getElementById(‘example‘));

上面代碼將一個 h1 標題,插入 example 節點(查看 demo01),運行結果如下。

三、JSX 文法

上一節的代碼, HTML 語言直接寫在 JavaScript 語言之中,不加任何引號,這就是 JSX 的文法,它允許 HTML 與 JavaScript 的混寫(查看 Demo02 )。

var names = [‘Alice‘, ‘Emily‘, ‘Kate‘];ReactDOM.render(  <div>  {    names.map(function (name) {      return <div>Hello, {name}!</div>    })  }  </div>,  document.getElementById(‘example‘));

上面代碼體現了 JSX 的基本文法規則:遇到 HTML 標籤(以 < 開頭),就用 HTML 規則解析;遇到代碼塊(以 { 開頭),就用 JavaScript 規則解析。上面代碼的運行結果如下。

JSX 允許直接在模板插入 JavaScript 變數。如果這個變數是一個數組,則會展開這個數組的所有成員(查看 demo03 )。

var arr = [  <h1>Hello world!</h1>,  <h2>React is awesome</h2>,];ReactDOM.render(  <div>{arr}</div>,  document.getElementById(‘example‘));

上面代碼的arr變數是一個數組,結果 JSX 會把它的所有成員,添加到模板,運行結果如下。

四、組件

React 允許將代碼封裝成組件(component),然後像插入普通 HTML 標籤一樣,在網頁中插入這個組件。React.createClass 方法就用於產生一個組件類(查看 demo04)。

var HelloMessage = React.createClass({  render: function() {    return <h1>Hello {this.props.name}</h1>;  }});ReactDOM.render(  <HelloMessage name="John" />,  document.getElementById(‘example‘));

上面代碼中,變數 HelloMessage 就是一個組件類。模板插入 <HelloMessage /> 時,會自動產生 HelloMessage 的一個執行個體(下文的"組件"都指組件類的執行個體)。所有組件類都必須有自己的 render 方法,用於輸出組件。

組件的用法與原生的 HTML 標籤完全一致,可以任意加入屬性,比如 <HelloMessage name="John" /> ,就是 HelloMessage 組件加入一個 name 屬性,值為 John。組件的屬性可以在組件類的 this.props 對象上擷取,比如 name 屬性就可以通過 this.props.name 讀取。上面代碼的運行結果如下。

添加組件屬性,有一個地方需要注意,就是 class 屬性需要寫成 className ,for 屬性需要寫成 htmlFor ,這是因為 class 和 for 是 JavaScript 的保留字。

五、this.props.children

this.props 對象的屬性與組件的屬性一一對應,但是有一個例外,就是 this.props.children 屬性。它表示組件的所有子節點(查看 demo05)。

var NotesList = React.createClass({  render: function() {    return (      <ol>      {        React.Children.map(this.props.children, function (child) {          return
  • {child}
;        })      }      </ol>    );  }});ReactDOM.render(  <NotesList>    <span>hello</span>    <span>world</span>  </NotesList>,  document.body);

上面代碼的 NoteList 組件有兩個 span 子節點,它們都可以通過 this.props.children 讀取,運行結果如下。

這裡需要注意, this.props.children 的值有三種可能:如果當前組件沒有子節點,它就是 undefined ;如果有一個子節點,資料類型是 object ;如果有多個子節點,資料類型就是 array 。所以,處理 this.props.children 的時候要小心。

React 提供一個工具方法 React.Children 來處理 this.props.children 。我們可以用 React.Children.map 來遍曆子節點,而不用擔心 this.props.children 的資料類型是 undefined 還是 object。更多的 React.Children 的方法,請參考官方文檔。

六、PropTypes

組件的屬性可以接受任意值,字串、對象、函數等等都可以。有時,我們需要一種機制,驗證別人使用組件時,提供的參數是否符合要求。

組件類的PropTypes屬性,就是用來驗證組件執行個體的屬性是否符合要求(查看 demo06)。

var MyTitle = React.createClass({  propTypes: {    title: React.PropTypes.string.isRequired,  },  render: function() {     return <h1> {this.props.title} </h1>;   }});

上面的Mytitle組件有一個title屬性。PropTypes 告訴 React,這個 title 屬性是必須的,而且它的值必須是字串。現在,我們設定 title 屬性的值是一個數值。

var data = 123;ReactDOM.render(  <MyTitle title={data} />,  document.body);

這樣一來,title屬性就通不過驗證了。控制台會顯示一行錯誤資訊。

Warning: Failed propType: Invalid prop `title` of type `number` supplied to `MyTitle`, expected `string`.

更多的PropTypes設定,可以查看官方文檔。

此外,getDefaultProps 方法可以用來設定組件屬性的預設值。

var MyTitle = React.createClass({  getDefaultProps : function () {    return {      title : ‘Hello World‘    };  },  render: function() {     return <h1> {this.props.title} </h1>;   }});ReactDOM.render(  <MyTitle />,  document.body);

上面代碼會輸出"Hello World"。

七、擷取真實的DOM節點

組件並不是真實的 DOM 節點,而是存在於記憶體之中的一種資料結構,叫做虛擬 DOM (virtual DOM)。只有當它插入文檔以後,才會變成真實的 DOM 。根據 React 的設計,所有的 DOM 變動,都先在虛擬 DOM 上發生,然後再將實際發生變動的部分,反映在真實 DOM上,這種演算法叫做 DOM diff ,它可以極大提高網頁的效能表現。

但是,有時需要從組件擷取真實 DOM 的節點,這時就要用到 ref 屬性(查看 demo07 )。

var MyComponent = React.createClass({  handleClick: function() {    this.refs.myTextInput.focus();  },  render: function() {    return (      <div>        <input type="text" ref="myTextInput" />        <input type="button" value="Focus the text input" onClick={this.handleClick} />      </div>    );  }});ReactDOM.render(  <MyComponent />,  document.getElementById(‘example‘));

上面代碼中,組件 MyComponent 的子節點有一個文本輸入框,用於擷取使用者的輸入。這時就必須擷取真實的 DOM 節點,虛擬 DOM 是拿不到使用者輸入的。為了做到這一點,文本輸入框必須有一個 ref 屬性,然後 this.refs.[refName] 就會返回這個真實的 DOM 節點。

需要注意的是,由於 this.refs.[refName] 屬性擷取的是真實 DOM ,所以必須等到虛擬 DOM 插入文檔以後,才能使用這個屬性,否則會報錯。上面代碼中,通過為組件指定 Click 事件的回呼函數,確保了只有等到真實 DOM 發生 Click 事件之後,才會讀取 this.refs.[refName] 屬性。

React 組件支援很多事件,除了 Click 事件以外,還有 KeyDown 、Copy、Scroll 等,完整的事件清單請查看官方文檔。

八、this.state

組件免不了要與使用者互動,React 的一大創新,就是將組件看成是一個狀態機器,一開始有一個初始狀態,然後使用者互動,導致狀態變化,從而觸發重新渲染 UI (查看 demo08 )。

var LikeButton = React.createClass({  getInitialState: function() {    return {liked: false};  },  handleClick: function(event) {    this.setState({liked: !this.state.liked});  },  render: function() {    var text = this.state.liked ? ‘like‘ : ‘haven\‘t liked‘;    return (      <p onClick={this.handleClick}>        You {text} this. Click to toggle.      </p>    );  }});ReactDOM.render(  <LikeButton />,  document.getElementById(‘example‘));

上面代碼是一個 LikeButton 組件,它的 getInitialState 方法用於定義初始狀態,也就是一個對象,這個對象可以通過 this.state 屬性讀取。當使用者點擊組件,導致狀態變化,this.setState 方法就修改狀態值,每次修改以後,自動調用 this.render 方法,再次渲染組件。

由於 this.props 和 this.state 都用於描述組件的特性,可能會產生混淆。一個簡單的區分方法是,this.props 表示那些一旦定義,就不再改變的特性,而 this.state 是會隨著使用者互動而產生變化的特性。

九、表單

使用者在表單填入的內容,屬於使用者跟組件的互動,所以不能用 this.props 讀取(查看 demo9 )。

var Input = React.createClass({  getInitialState: function() {    return {value: ‘Hello!‘};  },  handleChange: function(event) {    this.setState({value: event.target.value});  },  render: function () {    var value = this.state.value;    return (      <div>        <input type="text" value={value} onChange={this.handleChange} />        <p>{value}</p>      </div>    );  }});ReactDOM.render(<Input/>, document.body);

上面代碼中,文本輸入框的值,不能用 this.props.value 讀取,而要定義一個 onChange 事件的回呼函數,通過 event.target.value 讀取使用者輸入的值。textarea 元素、select元素、radio元素都屬於這種情況,更多介紹請參考官方文檔。

十、組件的生命週期

組件的生命週期分成三個狀態:

  • Mounting:已插入真實 DOM
  • Updating:正在被重新渲染
  • Unmounting:已移出真實 DOM

React 為每個狀態都提供了兩種處理函數,will 函數在進入狀態之前調用,did 函數在進入狀態之後調用,三種狀態共計五種處理函數。

  • componentWillMount()
  • componentDidMount()
  • componentWillUpdate(object nextProps, object nextState)
  • componentDidUpdate(object prevProps, object prevState)
  • componentWillUnmount()

此外,React 還提供兩種特殊狀態的處理函數。

  • componentWillReceiveProps(object nextProps):已載入組件收到新的參數時調用
  • shouldComponentUpdate(object nextProps, object nextState):組件判斷是否重新渲染時調用

這些方法的詳細說明,可以參考官方文檔。下面是一個例子(查看 demo10 )。

var Hello = React.createClass({  getInitialState: function () {    return {      opacity: 1.0    };  },  componentDidMount: function () {    this.timer = setInterval(function () {      var opacity = this.state.opacity;      opacity -= .05;      if (opacity < 0.1) {        opacity = 1.0;      }      this.setState({        opacity: opacity      });    }.bind(this), 100);  },  render: function () {    return (      <div style={{opacity: this.state.opacity}}>        Hello {this.props.name}      </div>    );  }});ReactDOM.render(  <Hello name="world"/>,  document.body);

上面代碼在hello組件載入以後,通過 componentDidMount 方法設定一個定時器,每隔100毫秒,就重新設定組件的透明度,從而引發重新渲染。

另外,組件的style屬性的設定方式也值得注意,不能寫成

style="opacity:{this.state.opacity};"

而要寫成

style={{opacity: this.state.opacity}}

這是因為 React 組件樣式是一個對象,所以第一重大括弧表示這是 JavaScript 文法,第二重大括弧表示樣式對象。

十一、Ajax

組件的資料來源,通常是通過 Ajax 請求從伺服器擷取,可以使用 componentDidMount 方法設定 Ajax 請求,等到請求成功,再用 this.setState 方法重新渲染 UI (查看 demo11 )。

var UserGist = React.createClass({  getInitialState: function() {    return {      username: ‘‘,      lastGistUrl: ‘‘    };  },  componentDidMount: function() {    $.get(this.props.source, function(result) {      var lastGist = result[0];      if (this.isMounted()) {        this.setState({          username: lastGist.owner.login,          lastGistUrl: lastGist.html_url        });      }    }.bind(this));  },  render: function() {    return (      <div>        {this.state.username}‘s last gist is        <a href={this.state.lastGistUrl}>here</a>.      </div>    );  }});ReactDOM.render(  <UserGist source="https://api.github.com/users/octocat/gists" />,  document.body);

上面代碼使用 jQuery 完成 Ajax 請求,這是為了便於說明。React 本身沒有任何依賴,完全可以不用jQuery,而使用其他庫。

我們甚至可以把一個Promise對象傳入組件,請看Demo12。

ReactDOM.render(  <RepoList    promise={$.getJSON(‘https://api.github.com/search/repositories?q=javascript&sort=stars‘)}  />,  document.body);

上面代碼從Github的API抓取資料,然後將Promise對象作為屬性,傳給RepoList組件。

如果Promise對象正在抓取資料(pending狀態),組件顯示"正在載入";如果Promise對象報錯(rejected狀態),組件顯示報錯資訊;如果Promise對象抓取資料成功(fulfilled狀態),組件顯示擷取的資料。

var RepoList = React.createClass({  getInitialState: function() {    return { loading: true, error: null, data: null};  },  componentDidMount() {    this.props.promise.then(      value => this.setState({loading: false, data: value}),      error => this.setState({loading: false, error: error}));  },  render: function() {    if (this.state.loading) {      return <span>Loading...</span>;    }    else if (this.state.error !== null) {      return <span>Error: {this.state.error.message}</span>;    }    else {      var repos = this.state.data.items;      var repoList = repos.map(function (repo) {        return (          <li>            <a href={repo.html_url}>{repo.name}</a> ({repo.stargazers_count} stars) <br/> {repo.description}          </li>        );      });      return (        <main>          <h1>Most Popular JavaScript Projects in Github</h1>          <ol>{repoList}</ol>        </main>      );    }  }});
十二、參考連結
  1. React‘s official site
  2. React‘s official examples
  3. React (Virtual) DOM Terminology, by Sebastian Markbåge
  4. The React Quick Start Guide, by Jack Callister
  5. Learning React.js: Getting Started and Concepts, by Ken Wheeler
  6. Getting started with React, by Ryan Clark
  7. React JS Tutorial and Guide to the Gotchas, by Justin Deal
  8. React Primer, by Binary Muse
  9. jQuery versus React.js thinking, by zigomir

轉自:http://www.ruanyifeng.com/blog/2015/03/react.html

React 入門執行個體教程

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.