翻譯至官方文檔《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
在教程中。咱們直接使用的是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'));
首先。你注意到的是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的縮寫)。
接下來,咱們就來建立這個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是傳遞給組件的命名屬性。
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端讀取的動態數據來替換硬性編碼的數據。咱們刪除了 data
屬性,改成採用 URL
來獲取:
// tutorial11.jsReact.render( <CommentBox url="comments.json" />, document.getElementById('content'));
這個組件和以前的組件的不一樣之處在於它必須預先自行渲染。
在從server端得到請求應答以前,它沒有可用的數據,而這些數據是組件呈現評論所必須的。
到現在爲止,所有的組件都僅僅是依據自身的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進行初始化。
在組件建立完成後,咱們還想要從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> ); }});
React向組件加入的事件處理函數使用的是駝峯命名規則。
咱們向表單加入了一個 onSumbit
的處理函數。它負責在輸入數據合法的表單提交後,將表單的字段清空。
在事件處理中。調用 preventDefault
是爲了阻止瀏覽器默認的與表單提交有關的行爲。
咱們使用 ref
屬性向子組件分配了一個名字,並且經過 this.refs
對組件進行引用。咱們可以在一個組件上調用 getDOMNode
獲取一個原生DOM元素。
當用戶提交一條評論時,咱們還需要對以前的評論列表進行更新。讓新的評論顯示進來。對於含有與呈現評論有關數據的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> ); }});
經過一些簡單的步驟,你已成功建立了一個評論盒子。