Learning React.js: Getting Started and Concepts
By Ken Wheeler (@ken_wheeler)
#簡介
今天我們開始一個新系列的學習,學習 React,我們將集中在怎麼熟練並且有效的使用臉書的 React 庫上。在我們開始實際寫程序之前,還有更重要的一步,我們先講解一些基本概念,好了,我們開始吧。
##什麼是 React?
React 是臉書開發的一個 UI 庫,可以用來方便創建具有交互性,狀態性 & 重用性的 UI 組件。它已經被運用在了臉書的生產環境上了,並且 Instagram.com 也完全是用 React 來寫的。
它的一個獨一無二的買點是,它不僅能用在客戶端,還能在服務端渲染,並且可以配合著用。
它還有一個概念叫做虛擬DOM,它會基於狀態更新選擇性的進行渲染子節點。這使得你的組件通過最少的DOM操作保持更新。
##虛擬DOM是怎樣工作的?
想象一下,你有一個人類模型。它有一個人應該有的各種屬性,並且反映了這個人當前的狀態。React 差不多就是這樣對待 DOM 的。
現在再想一下,如果你讓這個對象發生一些改變,比如說加個小鬍子,然後再加點肌肉,再加雙 Steve Buscemi 那樣的眼睛。在 React 世界,當你做了這些更新之後,有兩件事情會發生。首先, React 執行"髒值檢查",確定發生了什麼改變。然後第二步是調和,把檢測到的更新結果反映到 DOM 上。
React 的方式,不會真的去再生一個小孩出來,重新把他們養大,而只是做個臉和手臂的整形。也就是說,如果你在輸入框裏面的文字發生改變,除非輸入框的父節點發生渲染,否則文字將會保持原狀。
由於 React 用的是假的 DOM 而不是真的那個,那麽這就讓有了一種新的可能。我們可以在服務端來渲染這個假的 DOM,然後,duang~服務端的 React View。
#開始
想要開始用 React 很簡單,只要去下載他們提供的 starter kit 就好:
你也可以叉他們提供的 JSFiddle:
##頁面設置
那麽我們來設置頁面,你需要導入 react.js
和 JSXTransformer.js
,然後用 script 開始寫你的組件,記得把這個節點的 type
設置為 text/jsx
:
<!-- lang: js --> <!DOCTYPE html> <html> <head> <script src="build/react.js"></script> <script src="build/JSXTransformer.js"></script> </head> <body> <div id="mount-point"></div> <script type="text/jsx"> // React Code Goes Here </script> </body> </html>
在 React 裏面,組件必須加載到一個元素上,所以在例子裏面,我們可以用 div mount-point
來作為它的父容器。
當然這只是一個最簡單的起點,黨你實際上要寫點什麼的時候,最好用 Browerify 或者 webpack 這樣的工具來把你的組件拆分到不同的文件中。
#基礎
React 的基本單元模塊叫做組件(component),讓我們來寫一個:
<!-- lang: js --> <script type="text/jsx"> /** @jsx React.DOM */ React.renderComponent( <h1>Hello,world!</h1>,document.getElementById('myDiv') ); </script>
如果你沒看之前的代碼,那你肯定一頭霧水不知道這個 javascript/HTML 到底做了什麼。
##JSX
這就是所謂的 JSX,它是一種 Javascript XML 語法轉換,可以讓你在你的 Javascript 裏面寫 類HTML。我之所以說 類HTML 是有多重含義。你僅僅是基於一個關聯對象來寫 XML 。
對於正常的 html 標籤,在 JSX 中,class
屬性變成了 className
,而 for
則變成了 htmlFor
,這是因為有些 Javascript 的保留字。你可以看這裏來瞭解更多的不同。
如果你不用 JSX,那麽這裏是不用它的一個版本:
<!-- lang: js --> /** @jsx React.DOM */ React.renderComponent( React.DOM.h1(null,'Hello,world!'),document.getElementById('myDiv') );
在你的第一個代碼片段裏面,你有沒有注意到在頂行的 /** @jsx React.DOM */
?它非常重要,它告訴 React 我們用了 JSX 所以這段代碼需要轉換,所以你用 JSX 語法的時候,你需要把它包含進來。
##組件
黨使用上面的 renderComponent
方法,第一個參數是我們希望渲染的組件,第二個參數是將要掛載上去的DOM 節點。我們可以用 createClass
方法來創建自定義組件類。它可以使用對象作為參數。下面讓我們來創建一個:
<!-- lang: js --> var MyComponent = React.createClass({ render: function(){ return ( <h1>Hello,world!</h1> ); } });
創建一個類之後,我們可以在 document 中像這樣渲染它:
<!-- lang: js --> React.renderComponent( <MyComponent/>,document.getElementById('myDiv') );
屌不屌,嗯哼?
##Props
當我們使用我們定義的組件的時候,我們可以添加屬性,叫做 props,這些屬性在我們的組件仲可以通過 this.props
調用並且可以在我們的渲染方法裏面動態渲染數據:
<!-- lang: js --> var MyComponent = React.createClass({ render: function(){ return ( <h1>Hello,{this.props.name}!</h1> ); } }); React.renderComponent(<MyComponent name="Handsome" />,document.getElementById('myDiv'));
##Specs,生命週期 & 狀態
render
方法是創建組件唯一需要進行測試的部分,不過這裏有一些生命週期相關方法和測試我們可以用到,當我們在實際的組件中應該會有所幫助。
###生命週期方法
- componentWillMout 執行一次,在客戶端和服務端將要執行渲染的時候
- componentDidMount 執行一次,只在客戶端,當渲染完成之後
- shouldComponentUpdate 返回值判定組件是否需要更新的
- componentWillUnmount 在卸載之前執行
###Specs
- getInitialState 返回狀態初期值
- getDefaultProps 設置退避 props 值,如果 沒有可用 props
- mixins 一個對象數組,用來擴展當前組件的功能
###State
每個組件都有一個 state
對象和一個 props
對象。State 通過 setState
來設置。調用 setState
會觸發 UI 更新,它是 React 交互的基礎。如果我們想在所有交互開始之前設置一個初始狀態,我們可以用 getInitialState
方法。下面,讓我們看看我們是如何設置組件的狀態的:
<!-- lang: js --> var MyComponent = React.createClass({ getInitialState: function(){ return { count: 5 } },render: function(){ return ( <h1>{this.state.count}</h1> ) } });
##事件
React 還有一個內建跨瀏覽器事件系統。這些事件可以作為組件的屬性來追加也可以觸發方法。讓我們來通過事件來創建一個計數器:
<!-- lang: html --> <div id="mount-point"></div> <!-- lang: js --> /** @jsx React.DOM */ var Counter = React.createClass({ incrementCount: function(){ this.setState({ count: this.state.count + 1 }); },getInitialState: function(){ return { count: 0 } },render: function(){ return ( <div class="my-component"> <h1>Count: {this.state.count}</h1> <button type="button" onClick={this.incrementCount}>Increment</button> </div> ); } }); React.renderComponent(<Counter/>,document.getElementById('mount-point')); <!-- lang: css --> body { background: #bdc3c7; padding: 40px; } .counter { width: 300px; margin: auto; background: #9b59b6; color: white; padding: 20px; text-align: center; h1 { margin: 0; padding: 20px; font-size: 36px; } button { background: #f1c40f; border: 0; Box-shadow: 0px 5px 0px darken(#f1c40f,20%); padding: 20px; width: 100%; outline: none; border-radius: 3px; color: darken(#8e44ad,10%); font-weight: bold; } }
##單向數據流
在 React 中,英勇的數據通過 state
和 props
對象單向傳播,和其他的雙向綁定的庫完全不一樣,比如說 Angular。意思也就是說,在多組件模式下,一個共通父組件將負責管理狀態以及將它通過 props 向下傳播。
如果需要,你的狀態可以通過使用 setState
方法來更新,以確保 UI 的刷新被執行。結果值會被傳播到子組件,通過使用屬性。也就是說子組件可以用 this.props
來訪問的那些。
下面用一個例子來解釋這些概念:
<!-- lang: js --> /** @jsx React.DOM */ var FilteredList = React.createClass({ filterList: function(event){ var updatedList = this.state.initialItems; updatedList = updatedList.filter(function(item){ return item.toLowerCase().search( event.target.value.toLowerCase()) !== -1; }); this.setState({items: updatedList}); },getInitialState: function(){ return { initialItems: [ "Apples","Broccoli","Chicken","Duck","Eggs","Fish","Granola","Hash Browns" ],items: [] } },componentWillMount: function(){ this.setState({items: this.state.initialItems}) },render: function(){ return ( <div className="filter-list"> <input type="text" placeholder="Search" onChange={this.filterList}/> <List items={this.state.items}/> </div> ); } }); var List = React.createClass({ render: function(){ return ( <ul> { this.props.items.map(function(item) { return <li key={item}>{item}</li> }) } </ul> ) } }); React.renderComponent(<FilteredList/>,document.getElementById('mount-point')); <!-- lang: html --> <div id="mount-point"></div> <!-- lang: css --> * { Box-sizing: border-Box; } body { padding: 20px; background: #2c3e50; } .filter-list { margin: auto; width: 300px; background: #3498db; border-radius: 5px; border: 1px solid darken(#3498db,20%); input { width: 100%; display: block; padding: 10px; border-radius: 5px 5px 0px 0px; border: 0; font-size: 24px; &:focus { outline: none; } } ul { margin: 0; padding: 0; li { list-style-type: none; margin: 0; color: white; padding: 10px 20px; border-top: 1px solid darken(#3498db,20%); &:hover { background: #2980b9; } } } }
#總結
我們已經介紹了一些 React 的基礎知識了,花點時間去看看 React API 和學一下 JSX。
在學習 React 的下一張,我們將結合 Express 來創建一個在服務端渲染的應用,就像在客戶端一樣,然後使用 socket.io 來保持兩邊同步。
敬請期待!