React(有時稱爲React.js 或ReactJS)是一個爲數據提供渲染HTML視圖的開源JavaScript庫。javascript
它由FaceBook、Instagram和一個由我的開發者和企業組成的社羣維護,如今國外比較流行的Facebook、Imgur、Airbnb、uber、Instagram,國內的美團、阿里、大搜車、去哪兒等都在使用React技術。php
logo是個原子,中間是原子核,旁邊是3個電子的移動軌跡。html
React 起源於 Facebook 的內部項目,由於該公司對市場上全部 JavaScript MVC 框架,都不滿意,就決定本身寫一套,用來架設 Instagram 的網站。作出來之後,發現這套東西很好用,就在2013年5月開源了。java
React是解決什麼問題的,在官網能夠找到這樣一句話:react
We built React to solve one problem: building large applications with data that changes over time.git
React是由FaceBook的工程師Jordan Walke建立的,是受到php的HTML組件庫XHP影響,React在11年時,剛開始是部署在FaceBook的newsfeed;github
隨後在12年時部署於Instagram,於13年5月在JSConf US宣佈開源算法
14年成爲facebook第一個在Github上達到1萬star的旗艦開源項目,api
15年3月在JSConf,FaceBook發佈了React Native,可使用React來構建nativeApp,並提出本身的理念「learn once,write anywhere」。數組
16年4月,發佈V15正式版本,可是依然不夠穩定,這畢竟是最新的技術。
聲明式設計:採用聲明範式,能夠輕鬆描述應用
高效:經過對DOM的模擬,最大限度減小與DOM的交互
靈活:能夠方便的搭配其它庫來使用
JSX:是js語法的擴展
組件:構建組件,方便複用
單向相應的數據流
中文社區:http://reactjs.cn/
gitbook:https://www.gitbook.com/book/hulufei/react-tutorial/details
菜鳥教程:http://www.runoob.com/react/react-tutorial.html
書籍:React引領將來的用戶界面開發框架(資料中有pdf電子書)
React的核心思想是:封裝組件。
各個組件維護本身的狀態和UI,當狀態改變,自動從新繪製整個組件。
React中的核心概念:
概念1:組件
概念2:JSX
JavaScriptXml,不是新語言,也沒有改變js的語法,只是對js的擴展,
使用React,建議使用JSX語法,原生js也能夠,可是因爲JSX在定義相似HTML這種樹形結構時,十分簡單明瞭,所欲推薦使用JSX語法。
概念3:Virtual DOM
若是真的這樣大面積的操做 DOM,性能會是一個很大的問題,因此 React 實現了一個虛擬 DOM,組件 DOM 結構就是映射到這個虛擬 DOM 上,React 在這個虛擬 DOM 上實現了一個 diff 算法,當要更新組件的時候,會經過 diff 尋找到要變動的 DOM 節點,再把這個修改更新到瀏覽器實際的 DOM 節點上,因此實際上不是真的渲染整個 DOM 樹。這個虛擬 DOM 是一個純粹的 JS 數據結構,因此性能會比原生 DOM 快不少。
前面提到 virtual DOM 和真實的 DOM 有着不用的語義, 但同時也有明顯不一樣的 API。
DOM 樹上的節點被稱爲元素, 而 virtual DOM 是徹底不一樣的抽象, 叫作 components。
component 的使用在 React 裏極爲重要, 由於 components 的存在讓計算 DOM diff 更高效
舉例:
好比說,如今你的list是這樣,
<ul>
<li>0</li>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
你想把它變成這樣
<ul>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li>10</li>
</ul>
一般的操做是什麼?
先把0, 1,2,3這些Element刪掉,而後加幾個新的Element 6,7,8,9,10進去,這裏面就有4次Element刪除,5次Element添加。
而React會把這兩個作一下Diff,而後發現其實不用刪除0,1,2,3,而是能夠直接改innerHTML,而後只須要添加一個Element(10)就好了,這樣就是4次innerHTML操做加1個Element添加,比9次Element操做快多了!
概念4:Data Flow
單向數據綁定。是指數據更新後會自動渲染到頁面,避免在業務中頻繁的DOM操做。
依賴三個庫: react.js 、react-dom.js 和 Browser.js ;
它們必須首先加載。其中,react.js 是 React 的核心庫,react-dom.js 是提供與 DOM 相關的功能,Browser.js 的做用是將 JSX 語法轉爲 JavaScript 語法,
上面代碼有兩個地方須要注意。首先,最後一個 <script> 標籤的 type 屬性爲 text/babel 。這是由於 React 獨有的 JSX 語法,跟 JavaScript 不兼容。凡是使用 JSX 的地方,都要加上 type="text/babel"
建立1.html
ReactDOM.render( <h1> hello React </h1>, document.getElementById('example') );
ReactDOM.render(
<h1>Hello, world!</h1>,
document.getElementById('example')
);
ReactDOM.render 是 React 的最基本方法,用於將模板轉爲 HTML 語言,並插入指定的 DOM 節點
遇到HTML標籤(以<開頭),就用HTML來解析;遇到代碼塊(以{開頭)就用js來解析
JSX容許使用html語法來建立js對象,拿建立a標籤爲例,若是使用原生js,是這樣的:
React.createElement('a', {href: 'https://facebook.github.io/react/'}, 'Hello!')
當使用jsx語法時,就變成了這樣:
<a href="https://facebook.github.io/react/">Hello!</a>
JSX的基本語法規則:
接下來,建立2.html,驗證上述語法。
var names = ['daxu','dongdong','wenhua']; ReactDOM.render( <div> { names.map(function (name) { return <div> hello {name}</div> }) } </div>, document.getElementById('example') )
細心的同窗會發現,上邊的代碼運行時,會有個警告,是由於:
React爲了方便DOM渲染,對於每一個東西產生時都但願有一個單獨的key,咱們上邊沒有設置,因此會有警告,這裏先無論它。
咱們能夠在JSX中使用JavaScript表達式,表達式寫在{}中,好比說:建立3.html
var arr = [ <h1>react is awesome</h1>, <h2>let's start to learn!</h2> ] ReactDOM.render( < div > {/* 註釋必須卸載花括號中*/} <h1>{2+3}</h1> <h2>{3 == 2?'true':'false'}</h2> {/*若是在花括號中執行數組,數組會自動展開全部成員*/} {arr} </div >,document.getElementById('example') )
React 容許將代碼封裝成組件(component),而後像插入普通 HTML 標籤同樣,在網頁中插入這個組件。React.createClass 方法就用於生成一個組件類
建立4.html
上面代碼中,變量 HelloMsg 就是一個組件類。模板插入 < HelloMsg /> 時,會自動生成 HelloMsg 的一個實例(下文的"組件"都指組件類的實例)。全部組件類都必須有本身的 render 方法,用於輸出組件。
注意,組件類的第一個字母必須大寫,不然顯示會有問題,好比HelloMessage不能寫成helloMessage。另外,組件類只能包含一個頂層標籤,不然也會有問題。
在上述代碼上稍做修改:
組件的用法與原生的 HTML 標籤徹底一致,能夠任意加入屬性,好比 <HelloMsg name="daxu"> ,就是 HelloMsg 組件加入一個 name 屬性,值爲 daxu。組件的屬性能夠在組件類的 this.props 對象上獲取,好比 name 屬性就能夠經過 this.props.name 讀取。上面代碼的運行結果以下。
var HelloMsg = React.createClass({ render: function () { return <div><h1> hello {this.props.firstname+this.props.name}</h1> <p>come on!</p></div>; } }) ReactDOM.render( <HelloMsg name='daxu' firstname='zhao'/>,document.getElementById('example') );
this.props 對象的屬性與組件的屬性一一對應,可是有一個例外,就是 this.props.children 屬性。它表示組件的全部子節點
這裏須要注意, this.props.children 的值有三種可能:若是當前組件沒有子節點,它就是 undefined ;若是有一個子節點,數據類型是 object ;若是有多個子節點,數據類型就是 array 。因此,處理 this.props.children 的時候要當心。
React 提供一個工具方法 React.Children 來處理 this.props.children 。咱們能夠用 React.Children.map 來遍歷子節點,而不用擔憂 this.props.children 的數據類型是 undefined 仍是 object
建立5.html
var MyList = React.createClass({ render: function () { return( <ol> { React.Children.map(this.props.children, function (child) { return <li>{child}</li> }) } </ol> ); } }); ReactDOM.render( <MyList> <span>lesson1</span> <span>lesson2</span> <span>lesson3</span> </MyList>, document.getElementById('example') );
咱們能夠經過建立多個組件來合成一個組件,即把組件的不一樣功能點進行分離。
如下實例咱們實現了輸出網站名字和網址的組件:
建立6.html
組件並非真實的 DOM 節點,而是存在於內存之中的一種數據結構,叫作虛擬 DOM (virtual DOM)。只有當它插入文檔之後,纔會變成真實的 DOM 。根據 React 的設計,全部的 DOM 變更,都先在虛擬 DOM 上發生,而後再將實際發生變更的部分,反映在真實 DOM上,這種算法叫作 DOM diff ,它能夠極大提升網頁的性能表現
可是,有時須要從組件獲取真實 DOM 的節點,這時就要用到 ref
屬性
上面代碼中,組件 MyComponent 的子節點有一個文本輸入框,用於獲取用戶的輸入。這時就必須獲取真實的 DOM 節點,虛擬 DOM 是拿不到用戶輸入的。爲了作到這一點,文本輸入框必須有一個 ref 屬性,而後 this.refs.[refName] 就會返回這個真實的 DOM 節點。
須要注意的是,因爲 this.refs.[refName] 屬性獲取的是真實 DOM ,因此必須等到虛擬 DOM 插入文檔之後,才能使用這個屬性,不然會報錯。上面代碼中,經過爲組件指定 Click 事件的回調函數,確保了只有等到真實 DOM 發生 Click 事件以後,纔會讀取 this.refs.[refName] 屬性。
React 組件支持不少事件,除了 Click 事件之外,還有 KeyDown 、Copy、Scroll 等
組件免不了要與用戶互動,React的一大創新,就是將組件當作是一個狀態機,一開始就有一個初始狀態,而後用戶互動,致使狀態變化,從而觸發從新渲染UI。
具體實現起來,React裏有個state,只要更新組件的state,而後根據state從新渲染用戶界面(不要操做DOM),React來決定如何最高效的更新DOM。
針對text field的變化(用戶輸入)、服務器請求作出響應時,才須要用到state。
方法介紹:
方法1:getInitialState
定義初始狀態,也就是一個對象
方法2:setState 能夠獲取getInitialState中定義的對象,若是調用setState修改了狀態值,每次修改後,都將自動調用this.render方法,再次渲染組件
var LikeButton = React.createClass({ getInitialState: function () { return {liked:false}; }, handlerClick: function (event) { this.setState({liked:!this.state.liked}); }, render: function () { var text = this.state.liked?'like':'do not like'; return ( <p onClick={this.handlerClick}> You {text} this.click to toggle! </p> ); } }) ReactDOM.render( <LikeButton />,document.getElementById('example') )
上面代碼是一個 LikeButton
組件,它的 getInitialState
方法用於定義初始狀態,也就是一個對象,這個對象能夠經過 this.state
屬性讀取。當用戶點擊組件,致使狀態變化,this.setState
方法就修改狀態值,每次修改之後,自動調用 this.render
方法,再次渲染組件。
Mounting 已經插入真是DOM
Updating 正在被從新渲染
Unmounting 已經移出DOM
React 爲每一個狀態都提供了兩種處理函數:
一、 will 函數在進入狀態以前調用
二、 did 函數在進入狀態以後調用,
三種狀態共計五種處理函數:
8.3 案例分析
var Hello = React.createClass({ getInitialState: function () { return { opacity: 1.0 }; }, componentWillMount: function () { console.log('準備加入DOM'); }, componentDidMount: function () { console.log('已經加入DOM'); this.timer = setInterval(function () { var op = this.state.opacity; op-=0.5; if(op < 0.1) { op = 1.0; }; this.setState({ opacity: op }); }.bind(this),1000); }, render: function () { return( <div style={{opacity:this.state.opacity}}> hello {this.props.name} </div> ) } } ) ReactDOM.render(<Hello name='daxu'/>,document.getElementById('example'));
九、使用ref和state來實現一個乘法計算器
效果圖以下:
var LianXi = React.createClass({ getInitialState:function(){ return {result:0}; }, handlerClick: function () { this.setState({result:this.refs.num1.value*this.refs.num2.value}); }, render: function () { return ( <div> <input type='number' ref='num1'/> <input type='number' ref='num2'/> <button onClick={this.handlerClick}>求乘數</button> <p >{this.state.result}</p> </div> ) } }); ReactDOM.render(<LianXi /> ,document.getElementById('example'));