現在最熱門的前端框架有AngularJS、React、Bootstrap等。自從接觸了ReactJS,ReactJs的虛擬DOM(Virtual DOM)和元件化的開發深深的吸引了我,下面來跟我一起領略ReactJs的風采吧~~ 文章有點長,耐心讀完,你會有很大收穫哦~
React 起源於 Facebook 的內部專案,因為該公司對市場上所有 JavaScript MVC 框架,都不滿意,就決定自己寫一套,用來架設 Instagram 的網站。做出來以後,發現這套東西很好用,就在2013年5月開源了。由於 React 的設計思想極其獨特,屬於革命性創新,效能出眾,程式碼邏輯卻非常簡單。所以,越來越多的人開始關注和使用,認為它可能是將來 Web 開發的主流工具。
ReactJS官網地址:http://facebook.github.io/react/
Github地址:https://github.com/facebook/react
首先,對於React,有一些認識誤區,這裡先總結一下:
- React不是一個完整的MVC框架,最多可以認為是MVC中的V(View),甚至React並不非常認可MVC開發模式;
- React的伺服器端Render能力只能算是一個錦上添花的功能,並不是其核心出發點,事實上React官方站點幾乎沒有提及其在伺服器端的應用;
- 有人拿React和Web Component相提並論,但兩者並不是完全的競爭關係,你完全可以用React去開發一個真正的Web Component;
- React不是一個新的模板語言,JSX只是一個表象,沒有JSX的React也能工作。
1、ReactJS的背景和原理
在Web開發中,我們總需要將變化的資料實時反應到UI上,這時就需要對DOM進行操作。而複雜或頻繁的DOM操作通常是效能瓶頸產生的原因(如何進行高效能的複雜DOM操作通常是衡量一個前端開發人員技能的重要指標)。React為此引入了虛擬DOM(Virtual DOM)的機制:在瀏覽器端用Javascript實現了一套DOM API。基於React進行開發時所有的DOM構造都是通過虛擬DOM進行,每當資料變化時,React都會重新構建整個DOM樹,然後React將當前整個DOM樹和上一次的DOM樹進行對比,得到DOM結構的區別,然後僅僅將需要變化的部分進行實際的瀏覽器DOM更新。而且React能夠批處理虛擬DOM的重新整理,在一個事件迴圈(Event Loop)內的兩次資料變化會被合併,例如你連續的先將節點內容從A變成B,然後又從B變成A,React會認為UI不發生任何變化,而如果通過手動控制,這種邏輯通常是極其複雜的。儘管每一次都需要構造完整的虛擬DOM樹,但是因為虛擬DOM是記憶體資料,效能是極高的,而對實際DOM進行操作的僅僅是Diff部分,因而能達到提高效能的目的。這樣,在保證效能的同時,開發者將不再需要關注某個資料的變化如何更新到一個或多個具體的DOM元素,而只需要關心在任意一個資料狀態下,整個介面是如何Render的。
如果你像在90年代那樣寫過伺服器端Render的純Web頁面那麼應該知道,伺服器端所要做的就是根據資料Render出HTML送到瀏覽器端。如果這時因為使用者的一個點選需要改變某個狀態文字,那麼也是通過重新整理整個頁面來完成的。伺服器端並不需要知道是哪一小段HTML發生了變化,而只需要根據資料重新整理整個頁面。換句話說,任何UI的變化都是通過整體重新整理來完成的。而React將這種開發模式以高效能的方式帶到了前端,每做一點介面的更新,你都可以認為重新整理了整個頁面。至於如何進行區域性更新以保證效能,則是React框架要完成的事情。
借用Facebook介紹React的視訊中聊天應用的例子,當一條新的訊息過來時,傳統開發的思路如上圖,你的開發過程需要知道哪條資料過來了,如何將新的DOM結點新增到當前DOM樹上;而基於React的開發思路如下圖,你永遠只需要關心資料整體,兩次資料之間的UI如何變化,則完全交給框架去做。可以看到,使用React大大降低了邏輯複雜性,意味著開發難度降低,可能產生Bug的機會也更少。
2、元件化
虛擬DOM(virtual-dom)不僅帶來了簡單的UI開發邏輯,同時也帶來了元件化開發的思想,所謂元件,即封裝起來的具有獨立功能的UI部件。React推薦以元件的方式去重新思考UI構成,將UI上每一個功能相對獨立的模組定義成元件,然後將小的元件通過組合或者巢狀的方式構成大的元件,最終完成整體UI的構建。例如,Facebook的instagram.com整站都採用了React來開發,整個頁面就是一個大的元件,其中包含了巢狀的大量其它元件,大家有興趣可以看下它背後的程式碼。
如果說MVC的思想讓你做到檢視-資料-控制器的分離,那麼元件化的思考方式則是帶來了UI功能模組之間的分離。我們通過一個典型的Blog評論介面來看MVC和元件化開發思路的區別。
對於MVC開發模式來說,開發者將三者定義成不同的類,實現了表現,資料,控制的分離。開發者更多的是從技術的角度來對UI進行拆分,實現鬆耦合。
對於React而言,則完全是一個新的思路,開發者從功能的角度出發,將UI分成不同的元件,每個元件都獨立封裝。
在React中,你按照介面模組自然劃分的方式來組織和編寫你的程式碼,對於評論介面而言,整個UI是一個通過小元件構成的大元件,每個元件只關心自己部分的邏輯,彼此獨立。
React認為一個元件應該具有如下特徵:
(1)可組合(Composeable):一個元件易於和其它元件一起使用,或者巢狀在另一個元件內部。如果一個元件內部建立了另一個元件,那麼說父元件擁有(own)它建立的子元件,通過這個特性,一個複雜的UI可以拆分成多個簡單的UI元件;
(2)可重用(Reusable):每個元件都是具有獨立功能的,它可以被使用在多個UI場景;
(3)可維護(Maintainable):每個小的元件僅僅包含自身的邏輯,更容易被理解和維護;
ReactJs下載非常簡單,為了方便大家下載,這裡再一次給出下載地址http://facebook.github.io/react/downloads.html,下載完成後,我麼看到的是一個壓縮包。解壓後,我們新建一個html檔案,引用react.js和JSXTransformer.js這兩個js檔案。html模板如下(js路徑改成自己的):
<!DOCTYPE html> <html> <head> <script src="build/react.js"></script> <script src="build/JSXTransformer.js"></script> </head> <body> <div id="container"></div> <script type="text/jsx"> // ** Our code goes here! ** </script> </body> </html>
這裡大家可能會奇怪,為什麼script的type是text/jsx,這是因為 React 獨有的 JSX 語法,跟 JavaScript 不相容。凡是使用 JSX 的地方,都要加上 type="text/jsx" 。 其次,React 提供兩個庫: react.js 和 JSXTransformer.js ,它們必須首先載入。其中,JSXTransformer.js 的作用是將 JSX 語法轉為 JavaScript 語法。這一步很消耗時間,實際上線的時候,應該將它放到伺服器完成。
到這裡我們就可以開始編寫程式碼了,首先我們先來認識一下ReactJs裡面的React.render方法:
React.render 是 React 的最基本方法,用於將模板轉為 HTML 語言,並插入指定的 DOM 節點。
下面我們在script標籤裡面編寫程式碼,來輸出Hello,world,程式碼如下:
React.render( <h1>Hello, world!</h1>, document.getElementById('container') );
這裡需要注意的是,react並不依賴jQuery,當然我們可以使用jQuery,但是render裡面第二個引數必須使用JavaScript原生的getElementByID方法,不能使用jQuery來選取DOM節點。
然後,在瀏覽器開啟這個頁面,就可以看到瀏覽器顯示一個大大的Hello,world,因為我們用了<h1>標籤。
到這裡,恭喜,你已經步入了ReactJS的大門~~下面,讓我們來進一步學習ReactJs吧~~
HTML 語言直接寫在 JavaScript 語言之中,不加任何引號,這就是 JSX 的語法,它允許 HTML 與 JavaScript 的混寫,瞭解過AngularJs的看到下面的程式碼一定會感覺很熟悉的,我們來看程式碼:
var names = ['Jack', 'Tom', 'Alice']; React.render( <div> { names.map(function (name) { return <div>Hello, {name}!</div> }) } </div>, document.getElementById('container') );
這裡我們宣告瞭一個names陣列,然後遍歷在前面加上Hello,輸出到DOM中,輸出結果如下:
JSX 允許直接在模板插入 JavaScript 變數。如果這個變數是一個陣列,則會展開這個陣列的所有成員,程式碼如下:
var arr = [ <h1>Hello world!</h1>, <h2>React is perfect!</h2>, ]; React.render( <div>*{arr}*</div>, document.getElementById('container') );
顯示結果如下:
這裡的星號只是做標識用的,大家不要被她迷惑了~~
你看到這裡,說明你對React還是蠻感興趣的,恭喜你,堅持下來了,那麼下面,我們開始學習React裡面的"真功夫"了~~ Are you ready?
1、元件屬性
前面說了,ReactJS是基於元件化的開發,下面我們開始來學習ReactJS裡面的元件,React 允許將程式碼封裝成元件(component),然後像插入普通 HTML 標籤一樣,在網頁中插入這個元件。React.createClass 方法就用於生成一個元件類。
下面,我們來編寫第一個元件Greet,有一個name屬性,然後輸出hello + name的值,程式碼如下:
var Greet = React.createClass({ render: function() { return <h1>Hello {this.props.name}</h1>; } }); React.render( <Greet name="Jack" />, document.getElementById('container') );
看到這段程式碼,接觸過AngularJS的朋友們是不是有一種熟悉的感覺,不過這裡有幾點需要注意:
1、獲取屬性的值用的是this.props.屬性名
2、建立的元件名稱首字母必須大寫。
3、為元素新增css的class時,要用className.
4、元件的style屬性的設定方式也值得注意,要寫成style={{width: this.state.witdh}}
2、元件狀態
元件免不了要與使用者互動,React 的一大創新,就是將元件看成是一個狀態機,一開始有一個初始狀態,然後使用者互動,導致狀態變化,從而觸發重新渲染 UI 。下面我們來編寫一個小例子,一個文字框和一個button,通過點選button可以改變文字框的編輯狀態,禁止編輯和允許編輯。通過這個例子來理解ReactJS的狀態機制。先看程式碼:
var InputState = React.createClass({ getInitialState: function() { return {enable: false}; }, handleClick: function(event) { this.setState({enable: !this.state.enable}); }, render: function() { return ( <p> <input type="text" disabled={this.state.enable} /> <button onClick={this.handleClick}>Change State</button> </p> ); } }); React.render( <InputState />, document.getElementById('container') );
這裡,我們又使用到了一個方法getInitialState,這個函式在元件初始化的時候執行,必需返回NULL或者一個物件。這裡我們可以通過this.state.屬性名來訪問屬性值,這裡我們將enable這個值跟input的disabled繫結,當要修改這個屬性值時,要使用setState方法。我們宣告handleClick方法,來繫結到button上面,實現改變state.enable的值.效果如下:
原理分析:
當使用者點選元件,導致狀態變化,this.setState 方法就修改狀態值,每次修改以後,自動呼叫 this.render 方法,再次渲染元件。
這裡值得注意的幾點如下:
1、getInitialState函式必須有返回值,可以是NULL或者一個物件。
2、訪問state的方法是this.state.屬性名。
3、變數用{}包裹,不需要再加雙引號。
3、元件的生命週期
元件的生命週期分成三個狀態:
- Mounting:已插入真實 DOM
- Updating:正在被重新渲染
- Unmounting:已移出真實 DOM
React 為每個狀態都提供了兩種處理函式,will 函式在進入狀態之前呼叫,did 函式在進入狀態之後呼叫,三種狀態共計五種處理函式。
- componentWillMount()
- componentDidMount()
- componentWillUpdate(object nextProps, object nextState)
- componentDidUpdate(object prevProps, object prevState)
- componentWillUnmount()
此外,React 還提供兩種特殊狀態的處理函式。
- componentWillReceiveProps(object nextProps):已載入元件收到新的引數時呼叫
- shouldComponentUpdate(object nextProps, object nextState):元件判斷是否重新渲染時呼叫
下面來看一個例子:
var Hello = React.createClass({ getInitialState: function () { return { opacity: 1.0 }; }, componentDidMount: function () { this.timer = setInterval(function () { var opacity = this.state.opacity; opacity -= .05; if (opacity < 0.1) { opacity = 1.0; } this.setState({ opacity: opacity }); }.bind(this), 100); }, render: function () { return ( <div style={{opacity: this.state.opacity}}> Hello {this.props.name} </div> ); } }); React.render( <Hello name="world"/>, document.body );
上面程式碼在hello元件載入以後,通過 componentDidMount 方法設定一個定時器,每隔100毫秒,就重新設定元件的透明度,從而引發重新渲染。
4、元件的巢狀
React是基於元件化的開發,那麼元件化開發最大的優點是什麼?毫無疑問,當然是複用,下面我們來看看React中到底是如何實現元件的複用的,這裡我們還寫一個例子來說吧,程式碼如下:
var Search = React.createClass({ render: function() { return ( <div> {this.props.searchType}:<input type="text" /> <button>Search</button> </div> ); } }); var Page = React.createClass({ render: function() { return ( <div> <h1>Welcome!</h1> <Search searchType="Title" /> <Search searchType="Content" /> </div> ); } }); React.render( <Page />, document.getElementById('container') );
這裡我們建立了一個Search元件,然後又建立了一個Page元件,然後我們在Page元件中呼叫Search元件,並且呼叫了兩次,這裡我們通過屬性searchType傳入值,最終顯示結果如圖:
關於ReactJS今天就先學習到這裡了,下面來總結一下,主要有以下幾點:
1、ReactJs是基於元件化的開發,所以最終你的頁面應該是由若干個小元件組成的大元件。
2、可以通過屬性,將值傳遞到元件內部,同理也可以通過屬性將內部的結果傳遞到父級元件(留給大家研究);要對某些值的變化做DOM操作的,要把這些值放到state中。
3、為元件新增外部css樣式時,類名應該寫成className而不是class;新增內部樣式時,應該是style={{opacity: this.state.opacity}}而不是style="opacity:{this.state.opacity};"。
4、元件名稱首字母必須大寫。
5、變數名用{}包裹,且不能加雙引號。
React中文文件 http://reactjs.cn/
React 入門例項教程 http://www.ruanyifeng.com/blog/2015/03/react.html
顛覆式前端UI開發框架:React http://www.infoq.com/cn/articles/subversion-front-end-ui-development-framework-react
作者:雲霏霏
QQ交流群:243633526
部落格地址:http://www.cnblogs.com/yunfeifei/
宣告:本部落格原創文字只代表本人工作中在某一時間內總結的觀點或結論,與本人所在單位沒有直接利益關係。非商業,未授權,貼子請以現狀保留,轉載時必須保留此段宣告,且在文章頁面明顯位置給出原文連線。
如果大家感覺我的博文對大家有幫助,請推薦支援一把,給我寫作的動力。