react的工作原理

現在最熱門的前端框架有AngularJS、React、Bootstrap等。自從接觸了ReactJS,ReactJs的虛擬DOM(Virtual DOM)和組件化的開發深深的吸引了我,下面來跟我一起領略ReactJs的風采吧~~ 文章有點長,耐心讀完,你會有很大收穫哦~

 一、ReactJS簡介

  React 起源於 Facebook 的內部項目,因爲該公司對市場上所有 JavaScript MVC 框架,都不滿意,就決定自己寫一套,用來架設 Instagram 的網站。做出來以後,發現這套東西很好用,就在2013年5月開源了。由於 React 的設計思想極其獨特,屬於革命性創新,性能出衆,代碼邏輯卻非常簡單。所以,越來越多的人開始關注和使用,認爲它可能是將來 Web 開發的主流工具。

ReactJS官網地址:http://facebook.github.io/react/

 

Github地址:https://github.com/facebook/react

 

 二、對ReactJS的認識及ReactJS的優點

 首先,對於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,編寫Hello,world

   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吧~~

 

 三、Jsx語法

   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?

 

 四、ReactJS組件

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小結

關於ReactJS今天就先學習到這裏了,下面來總結一下,主要有以下幾點:

  1、ReactJs是基於組件化的開發,所以最終你的頁面應該是由若干個小組件組成的大組件。

  2、可以通過屬性,將值傳遞到組件內部,同理也可以通過屬性將內部的結果傳遞到父級組件(留給大家研究);要對某些值的變化做DOM操作的,要把這些值放到state中。

  3、爲組件添加外部css樣式時,類名應該寫成className而不是class;添加內部樣式時,應該是style={{opacity: this.state.opacity}}而不是style="opacity:{this.state.opacity};"。

  4、組件名稱首字母必須大寫。

  5、變量名用{}包裹,且不能加雙引號。

 

 六、ReactJS優缺點

優點:

React速度很快

與其它框架相比,React採取了一種特立獨行的操作DOM的方式。
它並不直接對DOM進行操作。
它引入了一個叫做虛擬DOM的概念,安插在JavaScript邏輯和實際的DOM之間。
這一概念提高了Web性能。在UI渲染過程中,React通過在虛擬DOM中的微操作來實對現實際DOM的局部更新。


跨瀏覽器兼容

虛擬DOM幫助我們解決了跨瀏覽器問題,它爲我們提供了標準化的API,甚至在IE8中都是沒問題的。


模塊化

爲你程序編寫獨立的模塊化UI組件,這樣當某個或某些組件出現問題是,可以方便地進行隔離。
每個組件都可以進行獨立的開發和測試,並且它們可以引入其它組件。這等同於提高了代碼的可維護性。

單向數據流讓事情一目瞭然

Flux是一個用於在JavaScript應用中創建單向數據層的架構,它隨着React視圖庫的開發而被Facebook概念化。它只是一個概念,而非特定工具的實現。它可以被其它框架吸納。例如,Alex Rattray有一個很好的Flux實例,在React中使用了Backbone的集合和模型。

純粹的JavaScript

現代Web應用程序與傳統的Web應用有着不同的工作方式。
例如,視圖層的更新需要通過用戶交互而不需要請求服務器。因此視圖和控制器非常依賴彼此。
許多框架使用Handlebars或Mustache等模板引擎來處理視圖層。但React相信視圖和控制器應該相互依存在一起而不是使用第三方模板引擎,而且,最重要的是,它是純粹的JavaScript程序。

同構的JavaScript


單頁面JS應用程序的最大缺陷在於對搜索引擎的索引有很大限制。React對此有了解決方案。
React可以在服務器上預渲染應用再發送到客戶端。它可以從預渲染的靜態內容中恢復一樣的記錄到動態應用程序中。
因爲搜索引擎的爬蟲程序依賴的是服務端響應而不是JavaScript的執行,預渲染你的應用有助於搜索引擎優化。

React與其它框架/庫兼容性好

比如使用RequireJS來加載和打包,而Browserify和Webpack適用於構建大型應用。它們使得那些艱難的任務不再讓人望而生畏。

缺點?

React本身只是一個V而已,所以如果是大型項目想要一套完整的框架的話,也許還需要引入Flux和routing相關的東西。

大多數坑沒踩出來

  


轉載於:雲霏霏 和 月迷津渡丶

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章