React JS高速新手教程


翻譯至官方文檔《Tutorial》http://facebook.github.io/react/docs/tutorial.htmljavascript

轉載請註明出處:http://blog.csdn.net/adousenhtml

推薦閱讀 React|RakNet 博客:http://blog.csdn.net/rsspub/article/category/1435601前端

教程演示樣例代碼,Web程序框架採用的是全棧python web框架Uliwebjava

Uliweb  https://github.com/adousen/reactjs_uliweb_examplepython



在新手教程裏,咱們會建立一個簡單卻有用的評論盒子來做爲咱們的樣例,你可以把它放進一個博客什麼的。react

它實際上就是Disqus、LiveFyre、Facebook等實時評論的基礎實現。
咱們要實現的功能有:jquery

  • 瀏覽所有的評論
  • 提交一個評論的表單
  • 爲你本身定義的後端提供一個鉤子

此外,另外一些優化特性:git

  • 優化評論:在評論保存到server前,就在列表中將其顯示。這樣會感受更快。
  • 實時更新:當其餘用戶作出評論後,評論列表就可以獲得實時的更新。

  • 支持Markdown格式:用戶可以用Markdown格式書寫內容。

第一步

在教程中。咱們直接使用的是CDN上的Javascript框架文件。
如下,打開隨意你喜歡的編輯器。建立一個新的HTML文檔:github

  
  
  
  
<!-- template.html --><html> <head> <title>Hello React</title> <script src="http://fb.me/react-0.12.0.js"></script> <script src="http://fb.me/JSXTransformer-0.12.0.js"></script> <script src="http://code.jquery.com/jquery-1.10.0.min.js"></script> </head> <body> <div id="content"></div> <script type="text/jsx"> // Your code here </script> </body></html>

此後的教程中。咱們都將在這裏的script標籤內編寫JavaScript代碼。web

注意
此處咱們將jQuery包括了進來,但目的僅僅是爲了方便編寫ajax調用。

但這不是在React中所必須作。

你的第一個組件

React所有的一切都是關於模塊化、複合化的組件。就咱們的評論功能來講。咱們將依照如下的組件結構來實現:

  
  
  
  
- CommentBox - CommentList - Comment - CommentForm

咱們先來建立一個CommentBox組件,它一開始僅僅是一個簡單的<div>:

  
  
  
  
// tutorial1.jsvar CommentBox = React.createClass({ render: function() { return ( <div className="commentBox"> Hello, world! I am a CommentBox. </div> ); }});React.render( <CommentBox />, document.getElementById('content'));

JSX 語法

首先。你注意到的是Javascript代碼中的XML化語法。

咱們實際上可以使用一個預編譯器來將此語法糖轉換爲純Javascript:

  
  
  
  
// tutorial1-raw.jsvar CommentBox = React.createClass({displayName: 'CommentBox', render: function() { return ( React.createElement('div', {className: "commentBox"}, "Hello, world! I am a CommentBox." ) ); }});React.render( React.createElement(CommentBox, null), document.getElementById('content'));

這是一種可選的方式,但實際上可以發現JSX語法要比單純的Javascript語法要簡單。

瞭解不少其它有關 JSX 語法的內容。

接下來作什麼

如下咱們要建立一個新的React組件,採取的方式是向 React.createClass() 傳遞一個Javascript對象。爲組件加入一些方法。當中最重要的一個方法是 render,它會返回一個React組件樹。並終於被渲染成HTML。


div 標籤並不是真正的DOM節點,它們僅僅是React div組件的實例。你可以把它想象成能由React識別並處理的一些標記或一段數據。

React是安全的。咱們並不生成HTML字符串。因此默認是XSS保護。


你可以返回一個由你或別人建立的組件樹,而不必定要返回主要的HTML。正因如此,React組件可以組合使用的:這是可維護前端的宗旨。


React.render() 初始化了一個根節點組件,而後啓動框架,並將標記注入到一個原生DOM元素中。這個DOM元素由第二個參數指定。

組建組件

接着咱們建立 CommentList 和 CommentForm 基本骨架,它們相同也是 div。注意,這段代碼要放在CommentBox 代碼的前面。

  
  
  
  
// tutorial2.jsvar CommentList = React.createClass({ render: function() { return ( <div className="commentList"> Hello, world! I am a CommentList. </div> ); }}); var CommentForm = React.createClass({ render: function() { return ( <div className="commentForm"> Hello, world! I am a CommentForm. </div> ); }});

下一步。更新 CommentBox 組件的代碼。使用新定義的兩個朋友:

  
  
  
  
// tutorial3.jsvar CommentBox = React.createClass({ render: function() { return ( <div className="commentBox"> <h1>Comments</h1> <CommentList /> <CommentForm /> </div> ); }});

注意咱們是怎樣混合使用HTML標籤和自建組件的。

HTML組件是規範的React組件。與本身定義的組件相似,僅僅是有一個區別:JSX編譯器會本身主動將HTML標籤重寫爲 React.createElement(tagName) 。並且不管其餘的事情。

這是了避免對全局命名空間的污染。

組件屬性

咱們將建立一個第三方組件 Comment,它負責接收評論者的名字和評論的內容。對於每個單獨的評論。咱們都可以重用這個組件的代碼。首先,咱們向 CommentList 加入一些評論。

  
  
  
  
// tutorial4.jsvar CommentList = React.createClass({ render: function() { return ( <div className="commentList"> <Comment author="Pete Hunt">This is one comment</Comment> <Comment author="Jordan Walke">This is *another* comment</Comment> </div> ); }});

注意到,這裏咱們經過父組件 CommentList 向子組件 Comment 傳遞了一些數據。比方,咱們在一個 Comment中,向其傳遞了Pete Hunt(經過屬性)和一條評論(經過XML格式的子節點)。從父組件向子組件傳遞的數據被稱爲props(單詞properties的縮寫)。

使用props

接下來,咱們就來建立這個Comment組件。使用porps咱們可以讀取從 CommentList 傳遞的數據,並渲染一些標記。

  
  
  
  
// tutorial5.jsvar Comment = React.createClass({ render: function() { return ( <div className="comment"> <h2 className="commentAuthor"> {this.props.author} </h2> {this.props.children} </div> ); }});

在JSX中經過用括號括起來的JavaScript表達式,可以將文本或者React組件(可以是一個屬性或子元素)放進組件樹中。

this.props 和嵌套元素 this.props.children 中的keywordprops是傳遞給組件的命名屬性。

加入 Markdowen語法支持

Markdown文本支持內聯樣式。好比。用星號圍起的文本可以強調顯示。
首先。咱們需要向程序中加入第三方的 Showdown 庫。這是一個支持Markdown並將其轉換爲原始HTML代碼的JavaScript庫。

咱們需要向head中加入一段script標籤(咱們已經包括了一些React的庫):

  
  
  
  
<!-- template.html --><head> <title>Hello React</title> <script src="http://fb.me/react-0.12.0.js"></script> <script src="http://fb.me/JSXTransformer-0.12.0.js"></script> <script src="http://code.jquery.com/jquery-1.10.0.min.js"></script> <script src="http://cdnjs.cloudflare.com/ajax/libs/showdown/0.3.1/showdown.min.js"></script></head>

下一步,咱們將評論的文本作Markdown轉換並輸出:

  
  
  
  
// tutorial6.jsvar converter = new Showdown.converter();var Comment = React.createClass({ render: function() { return ( <div className="comment"> <h2 className="commentAuthor"> {this.props.author} </h2> {converter.makeHtml(this.props.children.toString())} </div> ); }});

此處,咱們添加了對Showdown庫的調用。爲了將 this.props.children 從React包裝了的文本轉換成Showdown可以接受的原始字符串。咱們顯式地調用了 toString()
但是,這裏有一個問題需要解決。咱們最後渲染出來的評論內容在瀏覽器中看起來倒是這種形式:"<p>This is <em>another</em> comment</p>"。

咱們想要的是讓這些標籤都能被渲染爲實際的HTML。
這樣的處理方式是爲了防止XSS攻擊。

有一種方式可以跳過。但是框架會警告你不要使用這樣的方式。

  
  
  
  
// tutorial7.jsvar converter = new Showdown.converter();var Comment = React.createClass({ render: function() { var rawMarkup = converter.makeHtml(this.props.children.toString()); return ( <div className="comment"> <h2 className="commentAuthor"> {this.props.author} </h2> <span dangerouslySetInnerHTML={{__html: rawMarkup}} /> </div> ); }});

這個特殊的API的目的是讓插入原生的HTML代碼顯得困難,但是爲Showdown咱們仍是利用了這個後門。
記住:使用這個特徵時。你必須肯定Showdown是安全的。

鏈接數據模型

眼下爲止,咱們是直接在源碼中插入評論。如下,咱們將在評論列表中渲染一段JSON數據。終於,咱們將從server端獲取。但是現在,咱們把它直接寫在代碼中:

  
  
  
  
// tutorial8.jsvar data = [ {author: "Pete Hunt", text: "This is one comment"}, {author: "Jordan Walke", text: "This is *another* comment"}];

咱們需要以編寫模塊的方式將數據data加入進 CommentList.所以,咱們改動 CommentBox 組件 以及 React.render() 調用的代碼,將data經過props進行傳遞。

  
  
  
  
// tutorial9.jsvar CommentBox = React.createClass({ render: function() { return ( <div className="commentBox"> <h1>Comments</h1> <CommentList data={this.props.data} /> <CommentForm /> </div> ); }}); React.render( <CommentBox data={data} />, document.getElementById('content'));

現在,data已經被傳遞進了 CommentList,那麼讓咱們來動態地呈現評論數據:

  
  
  
  
// tutorial10.jsvar CommentList = React.createClass({ render: function() { var commentNodes = this.props.data.map(function (comment) { return ( <Comment author={comment.author}> {comment.text} </Comment> ); }); return ( <div className="commentList"> {commentNodes} </div> ); }});

就是這樣!

從server讀取

如下,咱們用從server端讀取的動態數據來替換硬性編碼的數據。咱們刪除了 data 屬性,改成採用 URL 來獲取:

  
  
  
  
// tutorial11.jsReact.render( <CommentBox url="comments.json" />, document.getElementById('content'));

這個組件和以前的組件的不一樣之處在於它必須預先自行渲染。

在從server端得到請求應答以前,它沒有可用的數據,而這些數據是組件呈現評論所必須的。

反應state

到現在爲止,所有的組件都僅僅是依據自身的props進行一次性的渲染。props 是不可變的:它們是從父組件傳遞過來,並且爲父組件所有。爲了實現交互。咱們爲組件引入了可變的 state 。this.state屬於組件的私有成員。並且可以經過調用 this.setState() 進行改動。當state更新以後。組件會立刻對其自身進行又一次渲染。
實際上在React代碼中。render() 方法被被聲明爲 this.props 和 this.state 的函數,並由框架保證了UI老是與輸入保持一致。
當從server取得數據後,就可以對咱們的評論數據進行改動。

首先,咱們向 CommentBox 組件的state加入一個包括評論數據的數組data:

  
  
  
  
// tutorial12.jsvar CommentBox = React.createClass({ getInitialState: function() { return {data: []}; }, render: function() { return ( <div className="commentBox"> <h1>Comments</h1> <CommentList data={this.state.data} /> <CommentForm /> </div> ); }});

在組件的生命週期中,getInitialState() 僅僅運行一次。它負責對組件的state進行初始化。

更新state

在組件建立完成後,咱們還想要從serverGET到JSON,從而更新state來反映最新的數據。

在實際的應用中。咱們可能建立的是一個動態的應用。但是,在樣例中爲了簡單。仍是使用一個靜態的JSON文件:

  
  
  
  
// tutorial13.json[ {"author": "Pete Hunt", "text": "This is one comment"}, {"author": "Jordan Walke", "text": "This is *another* comment"}]

咱們打算使用jQuery對server進行異步的訪問。
注意: 由於這是一個AJAX應用,所以你需要在一個webserver上執行,而不能仍停留在文件系統。

最簡單的方式是在應用的文件夾下執行python -m SimpleHTTPServer

  
  
  
  
// tutorial13.jsvar CommentBox = React.createClass({ getInitialState: function() { return {data: []}; }, componentDidMount: function() { $.ajax({ url: this.props.url, dataType: 'json', success: function(data) { this.setState({data: data}); }.bind(this), error: function(xhr, status, err) { console.error(this.props.url, status, err.toString()); }.bind(this) }); }, render: function() { return ( <div className="commentBox"> <h1>Comments</h1> <CommentList data={this.state.data} /> <CommentForm /> </div> ); }});

這裏的componentDidMount是一個在React組件渲染之後將被React調用的方法。動態更新的關鍵取決於 this.state的調用。在從server取得數據之後。咱們就使用新數組替換評論組件的舊數據,並且讓它動態地改變。這樣的反應的方式。使得動態更新僅僅是作了小小的改變。

此處,咱們使用的投票數據很是簡單,你也可以很是easy使用WebSockets或其餘技術來得到。

  
  
  
  
// tutorial14.jsvar CommentBox = React.createClass({ loadCommentsFromServer: function() { $.ajax({ url: this.props.url, dataType: 'json', success: function(data) { this.setState({data: data}); }.bind(this), error: function(xhr, status, err) { console.error(this.props.url, status, err.toString()); }.bind(this) }); }, getInitialState: function() { return {data: []}; }, componentDidMount: function() { this.loadCommentsFromServer(); setInterval(this.loadCommentsFromServer, this.props.pollInterval); }, render: function() { return ( <div className="commentBox"> <h1>Comments</h1> <CommentList data={this.state.data} /> <CommentForm /> </div> ); }}); React.render( <CommentBox url="comments.json" pollInterval={2000} />, document.getElementById('content')

此處咱們作的不過將AJAX調用放到一個獨立的方法中,並且在組件第一次載入和此後每隔兩秒調用一次。

可以嘗試在瀏覽器中執行一下,並且手動改動 comments.json。可以看到,在兩秒內變化就被呈現了出來。

加入新的評論

現在。是時候建立一個評論表單了。

咱們的 CommentForm 組件需要向詢問用戶他們的名字和評論的內容。並將其發送給server進行保存。

  
  
  
  
// tutorial15.jsvar CommentForm = React.createClass({ render: function() { return ( <form className="commentForm"> <input type="text" placeholder="Your name" /> <input type="text" placeholder="Say something..." /> <input type="submit" value="Post" /> </form> ); }});

讓咱們來建立與表單的交互。

當用戶點擊submit提交之後,咱們需要將表單清空。並將一個請求發送到server。而後更新評論列表。那麼,首先咱們需要監聽表單的submit事件。並將其清空。

  
  
  
  
// tutorial16.jsvar CommentForm = React.createClass({ handleSubmit: function(e) { e.preventDefault(); var author = this.refs.author.getDOMNode().value.trim(); var text = this.refs.text.getDOMNode().value.trim(); if (!text || !author) { return; } // TODO: send request to the server this.refs.author.getDOMNode().value = ''; this.refs.text.getDOMNode().value = ''; return; }, render: function() { return ( <form className="commentForm" onSubmit={this.handleSubmit}> <input type="text" placeholder="Your name" ref="author" /> <input type="text" placeholder="Say something..." ref="text" /> <input type="submit" value="Post" /> </form> ); }});

事件Events

React向組件加入的事件處理函數使用的是駝峯命名規則。

咱們向表單加入了一個 onSumbit 的處理函數。它負責在輸入數據合法的表單提交後,將表單的字段清空。
在事件處理中。調用 preventDefault 是爲了阻止瀏覽器默認的與表單提交有關的行爲。

Refs

咱們使用 ref 屬性向子組件分配了一個名字,並且經過 this.refs對組件進行引用。咱們可以在一個組件上調用 getDOMNode 獲取一個原生DOM元素。

在props中定義回調函數

當用戶提交一條評論時,咱們還需要對以前的評論列表進行更新。讓新的評論顯示進來。對於含有與呈現評論有關數據的state的CommentBox 來講,需要定義這種行爲邏輯。


咱們需要從子組件傳送數據到它的父組件。咱們在父組件的 render 方法中將一個新的回調函數(handleCommentSubmit)傳遞給子組件,並將其綁定在子組件的 onCommentSubmit 事件上。當事件被觸發後。回調函數就會被運行。

  
  
  
  
// tutorial17.jsvar CommentBox = React.createClass({ loadCommentsFromServer: function() { $.ajax({ url: this.props.url, dataType: 'json', success: function(data) { this.setState({data: data}); }.bind(this), error: function(xhr, status, err) { console.error(this.props.url, status, err.toString()); }.bind(this) }); }, handleCommentSubmit: function(comment) { // TODO: submit to the server and refresh the list }, getInitialState: function() { return {data: []}; }, componentDidMount: function() { this.loadCommentsFromServer(); setInterval(this.loadCommentsFromServer, this.props.pollInterval); }, render: function() { return ( <div className="commentBox"> <h1>Comments</h1> <CommentList data={this.state.data} /> <CommentForm onCommentSubmit={this.handleCommentSubmit} /> </div> ); }});

當用戶提交表單的時候,咱們就從 CommentForm 調用回調函數。

  
  
  
  
// tutorial18.jsvar CommentForm = React.createClass({ handleSubmit: function(e) { e.preventDefault(); var author = this.refs.author.getDOMNode().value.trim(); var text = this.refs.text.getDOMNode().value.trim(); if (!text || !author) { return; } this.props.onCommentSubmit({author: author, text: text}); this.refs.author.getDOMNode().value = ''; this.refs.text.getDOMNode().value = ''; return; }, render: function() { return ( <form className="commentForm" onSubmit={this.handleSubmit}> <input type="text" placeholder="Your name" ref="author" /> <input type="text" placeholder="Say something..." ref="text" /> <input type="submit" value="Post" /> </form> ); }});

現在。回調函數已經定義完成。

咱們要作的就是提交新的評論到server,並刷新評論列表。

  
  
  
  
// tutorial19.jsvar CommentBox = React.createClass({ loadCommentsFromServer: function() { $.ajax({ url: this.props.url, dataType: 'json', success: function(data) { this.setState({data: data}); }.bind(this), error: function(xhr, status, err) { console.error(this.props.url, status, err.toString()); }.bind(this) }); }, handleCommentSubmit: function(comment) { $.ajax({ url: this.props.url, dataType: 'json', type: 'POST', data: comment, success: function(data) { this.setState({data: data}); }.bind(this), error: function(xhr, status, err) { console.error(this.props.url, status, err.toString()); }.bind(this) }); }, getInitialState: function() { return {data: []}; }, componentDidMount: function() { this.loadCommentsFromServer(); setInterval(this.loadCommentsFromServer, this.props.pollInterval); }, render: function() { return ( <div className="commentBox"> <h1>Comments</h1> <CommentList data={this.state.data} /> <CommentForm onCommentSubmit={this.handleCommentSubmit} /> </div> ); }});

優化:優化更新

現狀咱們已經實現了這個應用的所有功能。

但是。在從server完畢請求以前,你必須等待評論在列表中出現。所以,會感受有點慢。咱們可以對它再作一點優化,讓它感受更快一點。

  
  
  
  
// tutorial20.jsvar CommentBox = React.createClass({ loadCommentsFromServer: function() { $.ajax({ url: this.props.url, dataType: 'json', success: function(data) { this.setState({data: data}); }.bind(this), error: function(xhr, status, err) { console.error(this.props.url, status, err.toString()); }.bind(this) }); }, handleCommentSubmit: function(comment) { var comments = this.state.data; var newComments = comments.concat([comment]); this.setState({data: newComments}); $.ajax({ url: this.props.url, dataType: 'json', type: 'POST', data: comment, success: function(data) { this.setState({data: data}); }.bind(this), error: function(xhr, status, err) { console.error(this.props.url, status, err.toString()); }.bind(this) }); }, getInitialState: function() { return {data: []}; }, componentDidMount: function() { this.loadCommentsFromServer(); setInterval(this.loadCommentsFromServer, this.props.pollInterval); }, render: function() { return ( <div className="commentBox"> <h1>Comments</h1> <CommentList data={this.state.data} /> <CommentForm onCommentSubmit={this.handleCommentSubmit} /> </div> ); }});

祝賀你。

經過一些簡單的步驟,你已成功建立了一個評論盒子。

你可以瞭解不少其它有關爲何使用React 或者深刻的學習 API參考。 祝你順利。

相關文章
相關標籤/搜索