在這一篇教學中我們會使用預先建置好放置在 CDN 的 Javascript 檔案。開啟你最愛的文字編輯器例如:Sublime text 然後建立一個 HTML 文件如下:
<!-- template.html --><html><head><title>Hello React</title><script src="http://fb.me/react-0.8.0.js"></script><script src="http://fb.me/JSXTransformer-0.8.0.js"></script><script src="http://code.jquery.com/jquery-1.10.0.min.js"></script></head><body><divid="content"></div><script type="text/jsx">/**
* @jsx React.DOM
*/// The above declaration must remain intact at the top of the script.
// 上面的宣告註解仍然得維持在 <script> 標簽中的頂部。
// Your code here!您的程式碼。
</script></body></html>
varCommentBox=React.createClass({render:function(){reutrn(React.DOM.div({className:'commentBox',children:'Hello, world! I am a CommentBox.'}));}});React.renderComponent(CommentBox({}),document.getElementById('content'))
小提醒:開發時我們會使用 JSXTransformer 以方便開發,而當要部署 Production 的時候,建議要先行編譯轉換以提升效能。
varCommentForm=React.createClass({handleSubmit:function(){varauthor=this.refs.author.getDOMNode().value.trim();vartext=this.refs.text.getDOMNode().value.trim();if(!text||!author){returnfalse;}// TODO: send request to the server
this.refs.author.getDOMNode().value='';this.refs.text.getDOMNode().value='';returnfalse;},render:function(){return(<formclassName="commentForm"onSubmit={this.handleSubmit}><inputtype="text"placeholder="Your name"ref="author"/><inputtype="text"placeholder="Say something..."ref="text"/><inputtype="submit"value="Post"/></form>);}});
varCommentBox=React.createClass({loadCommentsFromServer:function(){$.ajax({url:this.props.url,dataType:'json',success:function(data){this.setState({data:data});}.bind(this)});},handleCommentSubmit:function(comment){// TODO: submit to the server and refresh the list
},getInitialState:function(){return{data:[]};},componentWillMount:function(){this.loadCommentsFromServer();setInterval(this.loadCommentsFromServer,this.props.pollInterval);},render:function(){return(<divclassName="commentBox"><h1>Comments</h1><CommentListdata={this.state.data}/><CommentFormonCommentSubmit={this.handleCommentSubmit}/></div>);}});
getDefaultProps 將會把預設值暫存起來,以確保 this.props.value 有值。以上面的實際範例來說如果你不帶值則值會是 B ,如果有值則預設值會被取代。這使得你可以放心使用 props ,而無需反覆撰寫脆弱的代碼來處理元件。
傳遞 Props 的捷徑
常見的 React 元件是用基本 HTML 組合的延伸。通常你會想要傳遞屬性給 HTML 元素,React 提供了 transferPropsTo() 方法可以把屬性帶入讓你少打一些字。
/** @jsx React.DOM */varCheckLink=React.createClass({render:function(){// transferPropsTo() will take any props passed to CheckLink
// and copy them to <a>
returnthis.transferPropsTo(<a>{'√ '}{this.props.children}</a>);}});React.renderComponent(<CheckLinkhref="javascript:alert('Hello, world!');">Clickhere!</CheckLink>,document.getElementById('example'));
/** @jsx React.DOM */varCheckLink=React.createClass({render:function(){// transferPropsTo() will take any props passed to CheckLink
// and copy them to <a>
return(<ahref={this.props.href}>{'√ '}{this.props.children}</a>);}});React.renderComponent(<CheckLinkhref="javascript:alert('Hello, world!');">Clickhere!</CheckLink>,document.getElementById('example'));
value 一旦設定,你會發現不能改變了,像上面範例 value 永遠等於 Hello! 。任何輸入都無法改變值,因為 React 已經定義 value 是 Hello!,如果你想要讓 <input> 可以被使用者操作你應該使用 onChange 事件:
/** @jsx React.DOM */varTest=React.createClass({getInitialState:function(){return{value:'Hello!'}},handleChange:function(event){this.setState({value:event.target.value});},render:function(){// var value = this.state.value;
return(<inputtype='text'value={this.state.value}onChange={this.handleChange}/>);}});
讓我們整理一下你在目前學習過程可能會產生的疑惑,就是 props 和 state 的差異,首先是 props 不應該由元件本身去操作異動,應該只能夠在使用元件的時候當作帶入的參數(雖然的確是可以使用 setProps() 去設定,不過這違反官方的設計模式)。接著的工作就是負責傳遞資料。
而 this.state 可以當作參數傳遞資料也可以在元件內部去設置改變。換句話說,你應該只能在元件內部呼叫 setState() ,常見的應用都是用 this.state 處理關於使用者操作互動產生的結果。此外要注意的是如果你真的在子元件也使用了 state 那它跟主元件本身的狀態是分開獨立的。
另一個問題是為什麼 props 可以用 propTypes 驗證,而 state 沒有,主要是因為關於 state 完全是由元件設計者控制的,想像一下一般的情況下你設計了一個元件,而當其他人要使用的時候他應該只需要使用 React.renderComponent(<YourComponent />, document.getElementById('example')) 輸出元件即可,根據你提供的文件,它可以在屬性設定自己的參數 <YourComponent name="Andy" /> ,而 this.state 根本沒機會外露(修改元件除外)。所以如果你真的需要驗證 state 的時候就在適合的生命週期事件中處理就好了,例如 componentWillUpdate 事件。
根據上面這些說明,我們得到結論:就是一旦 <input> 的 value 被綁定就不會變動了,我們稱為元件約束,所以應該在 value 綁入一個變數,而這個變數按照模式的規劃應該使用 this.state 取得值是比較正確的。
/** @jsx React.DOM */
var Textarea = React.createClass({
render: function () {
return (
<textarea value='value' defaultValue='default'>This is dog</textarea>
);
}
});
React.renderComponent(<Textarea />, document.getElementById('example'));
其實我們這三個設定都可以用,但是子元素會等於 defaultValue 所以當子元素和 defaultValue 都用的時候會產生錯誤 If you supplydefaultValueon a <textarea>, do not pass children. 而 value 會覆寫 defaultValue 。為了避免混淆我們一般建議只用 value,接下來的用法就和上面提到的一樣。
// 錯誤範例: DO NOT DO THIS!
render:function(){varmyInput=<input/>;// 我可能會呼叫這個物件的 Method
this.rememberThisInput=myInput;// 在未來某個時間點我就可以直接呼叫他
return(<div><div>...</div>{myInput}</div>);}
OK 我們已經決定了這個元件程式最少的 state,下一步我們需要定義哪些元件是要變動的,以及是哪些要使用 state 。
記住!React 提供的是一種單向資料流的結構,通常是由上而下。剎那間可能不太輕易判斷哪個元件該管理或使用 state,這通常也是初學者最難理解的部分。
請跟著下面這些規則去推敲:
在傳統的網頁應用程式中,我們如果要增加互動性時勢必廣泛的操作 DOM 元素,一般來說現在最普遍的技術是使用 jQuery:
上圖我們故意讓 DOM 示意為紅色這是因為操作更新 DOM 是需要付出昂貴的代價,也意味著這很吃效能。
很多時候我們會使用 Model 來記錄關於 APP 狀態,不過通常我們最後目標是必須要將狀態呈現給使用者,所以我們必須自己實作這些細節。
這已經是我們很稀鬆平常的開發模式。
而 React 的主要目標就是提供一種不同且更有效率的方式去執行關於操作更新 DOM 這個部分,最終這個方式會取代我們直接操作 DOM 的方法。
React 使用的方式是透過建立一套虛擬 DOM 的機制,React 幫你處理關於操作 DOM 方面的事情。
為什麼多引進一層架構會讓效能增加? 如果在其架構之上多引入一層可以提升速度,這不是暗示瀏覽器並沒有實作最佳的 DOM 操作方式。
這也意味著虛擬 DOM 有著跟實際 DOM 不同的語義和行為。值得關注的是當我們改變虛擬 DOM 時並不能保證立即得到效果。
也因為這個機制導致 React 在實際接觸 DOM 之前必須要等待事件回圈結束。在同一時間它會去計算最小差異並盡可能的用最少的步驟去更新 DOM。
如此一來應用程式便能獨立執行批次更新,套用計算後的差異到實際 DOM 上,任何應用程式如果這麼做那麼都能夠像 React 一樣有效率。
但實際上自己編寫程式碼去做這些任務是很繁瑣且容易出錯,React 的精華之處就是幫你處理掉這些問題。
元件
就上面所提到的虛擬 DOM 的機制有著跟直接操作實際 DOM 不一樣的語義和行為,所以也會有明顯不同的 API。
所謂的元素即在 DOM 結構中的一個節點(node),不過在虛擬 DOM 機制底下一個節點完全是不一樣的東西,我們稱這個節點為元件。
使用元件對 React 來說是一件非常重要的事情,因為元件的設計概念是要拿來做計算的,就是計算和實際 DOM 的差異。
比起計算整個結構的差異,React 透過虛擬 DOM 將使得實際執行的時間複雜度大幅下降。
這個範例建立了一個 React 元件的類別(class): HelloMessage,然後透過 renderComponent() 在虛擬的 DOM 的機制中建立一個元件(<HelloMessage />, 本質上它就是 HelloMessage 類別實例化的物件,同時也是一個虛擬的 DOM)
最後把這個物件裝到真實的 DOM 元素(mountNode)。
首先是需要注意的事情是 React 的虛擬 DOM 通常來自您在應用程式中客制的元件(在這個例子是 <HelloMessage>)。這是一個意義重大的新嘗試,從內建的 DOM 分離出來。
DOM 通常不帶有任何程式邏輯,就只是一個被動的資料結構,且讓我們能夠附加處理事件。換句話說 React 的虛擬 DOM 是透過特定程式中的元件所創造的,且能夠加入程式中的特定 API 及內部邏輯。
這樣的方式比起直接修改操作 DOM ,例如: 使用 jQuery 的方式,這種建置 View 的方法是一種全新的抽象化方式與框架。
值得一提的是: 如果您一直持續關注 HTML 你也許知道關於 HTML 也許很快的也能自訂 DOM。
這將會帶給 DOM 類似的功能: 定義特定程式使用的 DOM 元素,不過 React 並不需要等到官方和瀏覽器完全實作這件事,因為虛擬 DOM 並不是真的 DOM。這讓 React 搶先在自訂元素與 Shadow DOM
這些功能實作普及之前您就能先用了。
回到我們的範例,我們已經建立了一個叫做 <HelloMessage> 的元件並且掛載 mountNode 裡面。
讓我們用圖片來說明初始化幾個部分的情形,首先,我們將虛擬 DOM 與實際 DOM 的關係視覺化,假設 mountNode 是網頁中的 <body> 標簽:
關於掛載(mount)一詞就理解為『對應』,把 a 掛載到 b 上 = 可以把 b 視為 a 。
箭頭表示虛擬元件已經被掛載到原生 DOM 元素中。這段過程非常短,不過也讓我們來看看關於應用程式的視圖部分的邏輯: