最近最火的前端UI框架非React莫屬了。趕緊找時(shí)間了解一下。
項目地址:http://facebook.github.io/react/
官方的介紹:A JavaScript library for building user interfaces (前端UI的js框架)
具有三個(gè)特點(diǎn):
Lots of people use React as the V in MVC. Since React makes no assumptions about the rest of your technology stack, it's easy to try it out on a small feature in an existing project. (一般人們將React用于實(shí)現前端MVC中的view中,因為React并不假定你使用的其它前端技術(shù)堆棧,所以很容易在已經(jīng)存在的項目中試用。)
React abstracts away the DOM from you, giving a simpler programming model and better performance. React can also render on the server using Node, and it can power native apps using React Native. (使用React我們并不直接操作瀏覽器中的DOM,React引入了虛擬DOM的概念,我們使用js代碼直接生成虛擬DOM節點(diǎn),然后React負責將虛擬DOM節點(diǎn)到實(shí)際DOM節點(diǎn)的轉換,這樣的話(huà),使前端UI編程更加的簡(jiǎn)單而且性能更好。)
React implements one-way reactive data flow which reduces boilerplate and is easier to reason about than traditional data binding.(React實(shí)現單向的數據流,從而減少了傳統數據綁定的模板文件,而且更加容易理解)
其實(shí)我自己的理解是:React是一個(gè)將前端UI組件化的框架,它可以將頁(yè)面上的各種UI實(shí)現為一個(gè)一個(gè)的組件,我們向組件傳入數據,React負責生成對應的UI,然后進(jìn)行渲染。實(shí)現方式是虛擬DOM和單向數據流,虛擬DOM提供了更好的性能,因為比如一個(gè)UI組件包含多個(gè)html標簽元素,如果我們需要修改其中的幾個(gè)標簽中的數據的話(huà),就會(huì )對DOM操作幾次(比如使用jquey),然而使用虛擬DOM,我們是先在內存中生成整個(gè)UI組件,然后一次性將虛擬DOM轉換渲染到實(shí)際DOM,所以對實(shí)際DOM的操作只有一次。單向數據流是指沒(méi)有數據的雙向綁定,數據只從Model向View傳送,每次更新都是使用一個(gè)全新的UI組件替換頁(yè)面上舊的UI組件,更加的簡(jiǎn)單。
其實(shí)React最大特點(diǎn)是:UI組件化和UI整體替換的更新方式,然后才是性能更好,更加簡(jiǎn)單。組件化就容易維護,方便重用。
實(shí)例:
<!DOCTYPE html><html><head><meta charset='utf-8'><script src='build/react.js'></script><script src='build/JSXTransformer.js'></script></head><body> <div id='example'></div> <script type='text/jsx'>React.render( <h1>Hello, world!</h1>, document.getElementById('example'));</script></body></html>
上面代碼導入React的js庫react.js,而JSXTransformer.js是將頁(yè)面上 type='text/jsx' 的代碼轉換成 text/javascript 代碼的轉換庫。轉換可以再瀏覽器端進(jìn)行,也可以使用Nodejs再服務(wù)端進(jìn)行。實(shí)際項目運行都是在服務(wù)端進(jìn)行轉換。我們先看下在瀏覽器端進(jìn)行轉換的效果:
可以看到React成功渲染出了一個(gè)UI組件,但是下面提示我們應該先在服務(wù)端預編譯 jsx 腳步。
然后我們再看服務(wù)端轉換預編譯 jsx 方法:
安裝Node.js:到 https://nodejs.org/ 下載對應平臺的安裝文件:node-v0.12.2-x86.msi
我是win7 64位,雙擊一路下一步安裝完成。配置系統路徑,將 D:\nodejs 加入 path, 新建系統變量:NODE_PATH,值為:D:\nodejs\node_modules\
安裝 react-tools: npm install react-tools
安裝成功如下圖:
再預編譯 jsx 腳本之前,我們將它提取出來(lái),單獨放入一個(gè)文件:helloworld.js
React.render( <h1>Hello, world!</h1>, document.getElementById('example'));然后調用react-tool中的 jsx 腳本來(lái)編譯:
上面將 當前目錄下的 jsxfile 文件夾中的所有的 jsx 腳本編譯到 當前目前下的 result 文件夾下,編譯之后的代碼為:
React.render( React.createElement('h1', null, 'Hello, world!'), document.getElementById('example'));React.createElement('h1', null, 'Hello, world!') 表示生產(chǎn)一個(gè) h1 標簽,內容為 'Hello, world!',然后React.render(),將生產(chǎn)的h1標簽渲染插入到document.getElementById('example')節點(diǎn)下去。最后的helloworld.html的內容為:
<!DOCTYPE html><html><head><meta charset='utf-8'><script src='build/react.js'></script></head><body> <div id='example'></div><script type='text/javascript'>React.render( React.createElement('h1', null, 'Hello, world!'), document.getElementById('example') // $('#example')[0]);</script></body></html>
注意這里我們將 JSXTransformer.js 文件去掉了,并且將 text/jsx 改成了 text/javascript. 當然我們也可以將編譯生產(chǎn)的js單獨放入一個(gè)文件,然后使用 <script type='text/javascript' src='xxx'> 引入到頁(yè)面。
效果為:
下面我們分析下Github上使用React實(shí)現的Tab選擇器
地址:https://github.com/supnate/react-tab-selector 該例子很好的說(shuō)明了React的基本用法
源碼如下:
<!DOCTYPE html><html><head><meta charset='utf-8'><script src='js/react.js'></script><script src='js/JSXTransformer.js'></script><link rel='stylesheet' tpe='text/css' href='style.css'/></head><body> <div id='container'></div> <script type='text/jsx'>var data = [ {name: 'Red', value: 'red'}, {name: 'Blue', value: 'blue'}, {name: 'Yellow', value: 'yellow'}, {name: 'Green', value: 'green'}, {name: 'White', value: 'White'}];var TabSelector = React.createClass({ getInitialState: function() { return {selected: this.props.selected}; }, handleOnClick: function (evt) { this.setState({'selected': evt.target.getAttribute('data-value')}) }, render: function() { var tabs = this.props.data.map(function (item) { var selected = item.value == this.state.selected ? 'selected' : ''; return <li data-value={item.value} className={selected} onClick={this.handleOnClick}> {item.name} </li>; }, this); return <div className='tab-selector'> <label>{this.props.label}</label> <ul>{tabs}</ul> </div>; }});React.render( TabSelector({label: 'Color', data: data, selected: null}), document.getElementById('container'));</script></body></html>
這個(gè)例子涉及到很多的React基礎知識:
TabSelector({label: 'Color', data: data, selected: null}) 相當于一個(gè)組件的構造函數,傳入我們從后臺獲得的數據(一個(gè)json對象),在組件TabSelector的實(shí)現中有各種函數,這些函數中訪(fǎng)問(wèn)我們傳入的數據的方式是:this.props.label, this.props.data, this.props.selected。render方法的實(shí)現,就是使用傳入的數據,然后返回我們需要在頁(yè)面上渲染的UI組件,this.props.data.map() 這里使用了javascript中數組的map函數來(lái)處理每一個(gè)數組項,每次得到 <li></li> ,所以返回的var tabs的結果是一個(gè) <li></li> 的數組。最后通過(guò)
return <div className='tab-selector'> <label>{this.props.label}</label> <ul>{tabs}</ul> </div>;
返回最終生成的UI組件。這里使用 var 定義的變量 tabs 的訪(fǎng)問(wèn)方式是:{tabs},訪(fǎng)問(wèn)傳入的數據的方式也是 {this.props.label}。每一個(gè)<li></li>標簽上定義了 onClick={this.handleOnClick} 點(diǎn)擊的處理方法,而 handleOnClick的實(shí)現為,每次點(diǎn)擊時(shí),就使用被點(diǎn)擊的<li></li>元素的data-value 屬性的值來(lái)更新自己的 className={selected} 中的 {selected},從而實(shí)現被點(diǎn)擊時(shí)就改變它的樣式。而這個(gè)實(shí)現中調用了setState()方法,這個(gè)方法是React庫中的方法,它被調用時(shí),如果state的狀態(tài)發(fā)生了變化,那么就會(huì )對UI組件使用新的state進(jìn)行重新渲染。
在getInitialState函數中定義了該組件的state的初始狀態(tài)(selected屬性的值),該函數在組件初始化時(shí)調用:
getInitialState: function() { return {selected: this.props.selected}; },而 this.setState({'selected': evt.target.getAttribute('data-value')}) 會(huì )修改該組件的狀態(tài)state的值,組件的state變化的話(huà),會(huì )導致組件UI的刷新,重新生成,然后插入DOM。實(shí)際頁(yè)面如下圖:
點(diǎn)擊某個(gè) tab 的效果如下圖:

所以總結一下:
1)getInitialState 函數用于定義組件的state;
2)render 函數定義了根據傳入的數據生成UI組件的方法;
3)而 setState 函數會(huì )修改組件的state,從而觸發(fā)組件UI的刷新,重新調用render方法;
4)handleOnClick 函數定義了點(diǎn)擊事件的處理;
5)訪(fǎng)問(wèn)傳入的數據的方法:{this.props.xx}
6)訪(fǎng)問(wèn)js變量的方法: {variable}
7)css 中的 class 要使用 className 替換,不能直接使用 class
8)React.createClass 創(chuàng )建組件(類(lèi)),使用了面向對象的概念來(lái)進(jìn)行處理,創(chuàng )建一個(gè)組件,就相當于實(shí)例化一個(gè)對象,類(lèi)就是“組件類(lèi)”,組件就是“對象”
9)訪(fǎng)問(wèn)state的屬性方法:{this.state.xxx}
10)React的jsx腳本的寫(xiě)法,和 html 頁(yè)面的寫(xiě)法是相似的,并不像在js中那樣需要引號來(lái)包含,這樣方便我們直接將前端的 html 頁(yè)面使用React來(lái)組件化
我們可以比較一下采用jquery的寫(xiě)法:
<!DOCTYPE html><html><head><meta charset='utf-8'><script src='js/jquery.min.js'></script><link rel='stylesheet' tpe='text/css' href='style.css'/></head><body> <div id='container'></div> <script type='text/javascript'>var data = [ {name: 'Red', value: 'red'}, {name: 'Blue', value: 'blue'}, {name: 'Yellow', value: 'yellow'}, {name: 'Green', value: 'green'}, {name: 'White', value: 'White'}];$.fn.TabSelector = function (options) { var arr = ['<div class='tab-selector'>']; arr.push('<label>', options.label, '</label>'); arr.push('<ul>'); options.data.forEach(function (item) { arr.push('<li data-value='', item.value, ''>'); arr.push(item.name); arr.push('</li>'); }); arr.push('</ul></div>'); this.html(arr.join('')); var lastSelected = null; this.on('click', 'li', function () { var $this = $(this); if (lastSelected) { lastSelected.removeClass('selected'); } $this.addClass('selected'); lastSelected = $this; }); return this;}$('#container').TabSelector({data: data, selected: null, label: 'Color'});</script></body></html>
明顯感覺(jué)到j(luò )query也就是javascript在拼接 html 代碼時(shí)的繁瑣,總是需要使用引號來(lái)處理,而React的jsx寫(xiě)法和html幾乎一樣。
有價(jià)值的參考鏈接:
React實(shí)現的理論知識:http://www.infoq.com/cn/articles/subversion-front-end-ui-development-framework-react
React的介紹:http://www.cnblogs.com/chyingp/p/react-getting-started.html
官網(wǎng):http://facebook.github.io/react/
聯(lián)系客服