React 入門實例教程

如今最熱門的前端框架,毫無疑問是 React 。javascript

上週,基於 React 的 React Native 發佈,結果一天以內,就得到了 5000 顆星,受矚目程度可見一斑。html

React 起源於 Facebook 的內部項目,由於該公司對市場上全部 JavaScript MVC 框架,都不滿意,就決定本身寫一套,用來架設Instagram 的網站。作出來之後,發現這套東西很好用,就在2013年5月開源了。前端

因爲 React 的設計思想極其獨特,屬於革命性創新,性能出衆,代碼邏輯卻很是簡單。因此,愈來愈多的人開始關注和使用,認爲它多是未來 Web 開發的主流工具。java

這個項目自己也越滾越大,從最先的UI引擎變成了一整套先後端通吃的 Web App 解決方案。衍生的 React Native 項目,目標更是宏偉,但願用寫 Web App 的方式去寫 Native App。若是可以實現,整個互聯網行業都會被顛覆,由於同一組人只須要寫一次 UI ,就能同時運行在服務器、瀏覽器和手機(參見《也許,DOM 不是答案》)。react

既然 React 這麼熱門,看上去充滿但願,固然應該好好學一下。從技術角度,能夠知足好奇心,提升技術水平;從職業角度,有利於求職和晉升,有利於參與潛力大的項目。可是,好的 React 教程卻不容易找到,這一方面由於這項技術太新,剛剛開始走紅,你們都沒有經驗,還在摸索之中;另外一方面由於 React 自己還在不斷變更,API 一直在調整,至今沒發佈1.0版。jquery

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

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

零、安裝

React 的安裝包,能夠到官網下載。不過,React Demos 已經自帶 React 源碼,不用另外安裝,只需把這個庫拷貝到你的硬盤就好了。算法

$ git clone .com:ruanyf/react-demos.git 

若是你沒安裝 git, 那就直接下載 zip 壓縮包後端

下面要講解的10個例子在各個 Demo 子目錄,每一個目錄都有一個 index.html 文件,在瀏覽器打開這個文件(大多數狀況下雙擊便可),就能馬上看到效果。

須要說明的是,React 能夠在瀏覽器運行,也能夠在服務器運行,可是本教程只涉及瀏覽器。一方面是爲了儘可能保持簡單,另外一方面 React 的語法是一致的,服務器的用法與瀏覽器差異不大。Demo11 是服務器首屏渲染的例子,有興趣的朋友能夠本身去看源碼。

1、HTML 模板

使用 React 的網頁源碼,結構大體以下。

<!DOCTYPE html> <html> <head> <script src="../build/react.js"></script> <script src="../build/JSXTransformer.js"></script> </head> <body> <div id="example"></div> <script type="text/jsx">  // ** Our code goes here! ** </script> </body> </html> 

上面代碼有兩個地方須要注意。首先,最後一個 script 標籤的 type 屬性爲 text/jsx 。這是由於 React 獨有的 JSX 語法,跟 JavaScript 不兼容。凡是使用 JSX 的地方,都要加上 type="text/jsx" 。

其次,React 提供兩個庫: react.js 和 JSXTransformer.js ,它們必須首先加載。其中,JSXTransformer.js 的做用是將 JSX 語法轉爲 JavaScript 語法。這一步很消耗時間,實際上線的時候,應該將它放到服務器完成。

$ jsx src/ build/ 

上面命令能夠將 src 子目錄的 js 文件進行語法轉換,轉碼後的文件所有放在 build 子目錄。

2、React.render()

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

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

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

3、JSX 語法

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

var names = ['Alice', 'Emily', 'Kate']; React.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>, ]; React.render( <div>{arr}</div>, document.getElementById('example') ); 

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

4、組件

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

var HelloMessage = React.createClass({ render: function() { return <h1>Hello {this.props.name}</h1>; } }); React.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 的保留字。

5、this.props.children

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

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

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

這裏須要注意,只有當子節點多餘1個時,this.props.children 纔是一個數組,不然是不能用 map 方法的, 會報錯。

6、React.findDOMNode()

組件並非真實的 DOM 節點,而是存在於內存之中的一種數據結構,叫作虛擬 DOM (virtual DOM)。只有當它插入文檔之後,纔會變成真實的 DOM 。根據 React 的設計,全部的 DOM 變更,都先在虛擬 DOM 上發生,而後再將實際發生變更的部分,反映在真實 DOM上,這種算法叫作 DOM diff ,它能夠極大提升網頁的性能表現。

可是,有時須要從組件獲取真實 DOM 的節點,這時就要用到 React.findDOMNode 方法(查看 demo06 )。

var MyComponent = React.createClass({ handleClick: function() { React.findDOMNode(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> ); } }); React.render( <MyComponent />, document.getElementById('example') ); 

上面代碼中,組件 MyComponent 的子節點有一個文本輸入框,用於獲取用戶的輸入。這時就必須獲取真實的 DOM 節點,虛擬 DOM 是拿不到用戶輸入的。爲了作到這一點,文本輸入框必須有一個 ref 屬性,而後 this.refs.[refName] 就指向這個虛擬 DOM 的子節點,最後經過 React.findDOMNode 方法獲取真實 DOM 的節點。

須要注意的是,因爲 React.findDOMNode 方法獲取的是真實 DOM ,因此必須等到虛擬 DOM 插入文檔之後,才能使用這個方法,不然會返回 null 。上面代碼中,經過爲組件指定 Click 事件的回調函數,確保了只有等到真實 DOM 發生 Click 事件以後,纔會調用 React.findDOMNode 方法。

React 組件支持不少事件,除了 Click 事件之外,還有 KeyDown 、Copy、Scroll 等,完整的事件清單請查看官方文檔

7、this.state

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

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> ); } }); React.render( <LikeButton />, document.getElementById('example') ); 

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

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

8、表單

用戶在表單填入的內容,屬於用戶跟組件的互動,因此不能用 this.props 讀取(查看 demo08 )。

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> ); } }); React.render(<Input/>, document.body); 

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

9、組件的生命週期

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

  • 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):組件判斷是否從新渲染時調用

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

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> ); } }); React.render( <Hello name="world"/>, document.body ); 

上面代碼在hello組件加載之後,經過 componentDidMount 方法設置一個定時器,每隔100毫秒,就從新設置組件的透明度,從而引起從新渲染。

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

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

而要寫成

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

這是由於 React 組件樣式是一個對象,因此第一重大括號表示這是 JavaScript 語法,第二重大括號表示樣式對象。

10、Ajax

組件的數據來源,一般是經過 Ajax 請求從服務器獲取,可使用 componentDidMount 方法設置 Ajax 請求,等到請求成功,再用 this.setState 方法從新渲染 UI (查看 demo10 )。

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> ); } }); React.render( <UserGist source="https://api.github.com/users/octocat/gists" />, document.body ); 

上面代碼使用 jQuery 完成 Ajax 請求,這是爲了便於說明。React 沒有任何依賴,徹底可使用其餘庫。

11、參考連接

  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

(完)

相關文章
相關標籤/搜索