過去css
須要手動更新DOM、費力地記錄每個狀態;既不具有擴展性,又很難加入新的功能,就算能夠,也是有着冒着很大的風險。html
不過,使用這種開發方式很難打造出極佳的用戶體驗。由於不管每次用戶想要作點什麼,都須要向服務端發送請求並等待服務端的響應,這會致使用戶失去在頁面上所積累的狀態。前端
Reactreact
它引入了一種新的方式來處理瀏覽器DOM。程序員
在任什麼時候間點,React都能以最小的DOM修改來更新整個應用程序。web
React本質上只關心兩件事:1). 更新DOM;2). 響應事件。ajax
每次狀態改變時,使用JavaScript從新渲染整個頁面會很是慢,這應該歸咎於讀取和更新DOM的性能問題。React運用一個虛擬的DOM實現了一個很是強大的渲染系統,在React中對DOM只更新不讀取。算法
React不處理Ajax、路由和數據存儲,也不規定數據組織的方式。它不是一個Model-View-Controller框架。若是非要問它是什麼,他就是MVC裏的「V」。React的精簡容許你將它集成到各類各樣的系統中 。編程
React以渲染函數爲基礎。這些函數讀入當前的狀態,將其轉換爲目標頁面上的一個虛擬表現。redux
只要React被告知狀態有變化,他就會從新運行這些函數,計算出頁面的一個新的虛擬表現,接着自動把結果轉換成必要的DOM更新來反映新的表現。【不須要reset方式的completely update】
這種方式看上去應該比一般的JavaScript方案——按須要更新每個元素——要慢,可是React確實是這麼作的:它使用了很是高效的算法,計算出虛擬頁面當前版本和新版間的差別,基於這些差別對DOM進行必要的最少更新。
公認的性能瓶頸
"React除了可以組件化開發ui,還完徹底全實現了先後端的隔離"。客官,此話怎講?
Ref: 怎樣理順react,flux,redux這些概念的關係,開發中有必要使用它們嗎?
Ref: 理解 React,但不理解 Redux,該如何通俗易懂的理解 Redux?
1. 反覆刷新頁面,尤爲是內容複雜的頁面,對瀏覽器的渲染性能消耗很大。
2. 由交互產生的不少細膩的前端數據,其實也很難交給後臺處理,由於這是咱們無處安放的臨時狀態
1. 根據後臺數據排版生成頁面,如今只是最基本的工做。
2. 當用戶進行某種交互操做引發頁面狀態變化以後,爲了不頁面總體刷新,咱們須要當心翼翼地將各個相關的頁面局部元素揀選出來,作適當修改,再放回原處,動做通常不會太優雅。
1. 根據肯定的交互狀態(state),一古腦兒決定頁面的呈現(view),這種「單向流」的開發狀態對程序員來講是思惟清晰、比較輕鬆的;一旦咱們須要不斷手動更新view,而且改變state和view的代碼還糾纏在一塊兒,咱們的心裏每每是崩潰的。
2. 爲了讓前端開發不感到崩潰,就把全部state交給後臺來維護,簡單粗暴地經過從新加載頁面來實現view的更新是不靠譜的,咱們須要找到新的方法,來實現view的自動更新。
我腦補着facebook的某個程序員在一個月黑風高的晚上坐在公司電腦前,抿了一口濃濃的咖啡,忽然靈光一現,伴着屏幕上忽明忽暗的幽幽藍光,在文本編輯器裏寫下這麼一行文字:
可不能夠把瀏覽器裏的DOM tree克隆一份完整的鏡像到內存,也就是所謂的「virtual DOM」,當頁面的state發生變化之後,根據最新的state從新生成一份virtual DOM(至關於在內存裏「刷新」整個頁面),將它和以前的virtual DOM作比對(diff),而後在瀏覽器裏只渲染被改變的那部份內容,這樣瀏覽器的性能損耗和用戶體驗不就都不成問題了嗎?
而咱們知道在絕大部分網頁應用中js引擎的性能和內存徹底沒有被充分利用,咱們正好能夠火力全開,利用js的這部分性能紅利,實現內存中virtual DOM的diff工做,完美!
因而React橫空出世!伴隨着react的崛起,相似於redux這些專一於管理state的輕量級框架也變得煊赫一時起來。
決定頁面呈現的state能夠經過模塊屬性(props)從父模塊傳遞到子模塊。
這種"樹狀"分流機制,有點像植物將營養(state)從根部不斷運輸到細枝末葉的過程。
* 傳統MVC
1. 前端開發的Model至關於後臺數據的鏡像或緩存池,它和服務器端MVC中的Model概念一脈相承;
2. View對應頁面的呈現,主要指的是和html、css相關的代碼,它和服務器端MVC中的View概念也很是相近。
3. 顯著的差異來自於controller:在後臺應用中,用戶和服務器之間的交互是經過http請求實現的,所以後臺controller的表達形式是http請求的handler,而且和router(定義網站的url規則)緊密相關; 而前端應用中,用戶和網頁之間的交互主要是經過操做事件(例如點擊鼠標、鍵盤輸入等)實現的,所以前端的controller這裏能夠簡單理解爲各類交互事件的handler。
* 問題表象
修改Model的Controller代碼像一把黃豆同樣散落在了各個View組件的內部。
* 問題緣由
若是能夠用某種方式把這些散落的代碼單獨收攏到一塊兒,是否是就讓這可讓這張圖示恢復秩序呢?好,咱們順着這個思路想下去。
不是MVC模式錯了,而是咱們壓根缺乏了一個和用戶交互行爲有關的action抽象!所以,對model的具體操做才無法從各個view組件中被剝離出來,放到一處。
* Flux思想 - 單向流思想
1. flux與react沒有直接的關係,兩者是徹底獨立的概念。
2. flux不是一個js庫,而是一種前端代碼的組織思想,好比說 redux庫能夠認爲是一種flux思想的實現。
從代碼層面而言,flux無非就是一個常見的event dispatcher,
其目的是要將以往MVC中各個View組件內的controller代碼片段提取出來放到更加恰當的地方進行集中化管理,並從開發體驗上實現了溫馨清爽、容易駕馭的「單向流」模式。
用戶在view上的交互行爲(好比點擊提交按鈕等)應當引發state改變的時候,這個流程該怎麼處理?
首先,react框架爲咱們理順了 store --> view 的「單向」工做流(store是state的容器);
而後,redux框架爲咱們理順了 view --> store 的**「單向」**工做流。
而且,react和redux都以組件化的形式能夠將各自負責的功能進行靈活地組裝或拆分,最大程度上確保咱們「一次只須要專一於一個局部問題」。具體來講,分爲如下步驟:
從需求出發,看看使用React須要什麼:
1. React有props和state:
props 意味着:父級分發下來的屬性;
state 意味着:組件內部能夠自行管理的狀態;
而且整個React沒有數據向上回溯的能力,也就是說數據只能單向向下分發,或者自行內部消化。
理解這個是理解React和Redux的前提。
2. 通常構建的React組件內部多是一個完整的應用,它本身工做良好,你能夠經過屬性做爲API控制它。可是更多的時候發現React根本沒法讓兩個組件互相交流,使用對方的數據。
而後這時候不經過DOM溝通(也就是React體制內)解決的惟一辦法就是提高state,將state放到共有的父組件中來管理,再做爲props分發回子組件。
3. 子組件改變父組件state的辦法只能是經過onClick觸發父組件聲明好的回調,
也就是父組件提早聲明好函數或方法做爲契約描述本身的state將如何變化,
再將它一樣做爲屬性交給子組件使用。
這樣就出現了一個模式:數據老是單向從頂層向下分發的,可是隻有子組件回調在概念上能夠回到state頂層影響數據。這樣state必定程度上是響應式的。
4. 爲了面臨全部可能的擴展問題,最容易想到的辦法就是把全部state集中放到全部組件頂層,而後分發給全部組件。
5. 爲了有更好的state管理,就須要一個庫來做爲更專業的頂層state分發給全部React應用,這就是Redux。
讓咱們回來看看重現上面結構的需求:
a. 須要回調通知state (等同於回調參數) -> action【發起的通訊請求】
b. 須要根據回調處理 (等同於父級方法) -> reducer 【對通訊請求刷選處理的過程】
c. 須要state (等同於總狀態) -> store
對Redux來講只有這三個要素:
a. action是純聲明式的數據結構,只提供事件的全部要素,不提供邏輯。
b. reducer是一個匹配函數,action的發送是全局的:全部的reducer均可以捕捉到並匹配與本身相關與否,相關就拿走action中的要素進行邏輯處理,修改store中的狀態,不相關就不對state作處理原樣返回。
c. store負責存儲狀態並能夠被react api回調,發佈action.
固然通常不會直接把兩個庫拿來用,還有一個binding叫react-redux, 提供一個Provider和connect。不少人其實看懂了redux卡在這裏。
a. Provider是一個普通組件,能夠做爲頂層app的分發點,它只須要store屬性就能夠了。它會將state分發給全部被connect的組件,無論它在哪裏,被嵌套多少層。
b. connect是真正的重點,它是一個科裏化函數,意思是先接受兩個參數(數據綁定mapStateToProps和事件綁定mapDispatchToProps),再接受一個參數(將要綁定的組件自己):
mapStateToProps:構建好Redux系統的時候,它會被自動初始化,可是你的React組件並不知道它的存在,所以你須要分揀出你須要的Redux狀態,因此你須要綁定一個函數,它的參數是state,簡單返回你關心的幾個值。
mapDispatchToProps:聲明好的action做爲回調,也能夠被注入到組件裏,就是經過這個函數,它的參數是dispatch,經過redux的輔助方法bindActionCreator綁定全部action以及參數的dispatch,就能夠做爲屬性在組件裏面做爲函數簡單使用了,不須要手動dispatch。這個mapDispatchToProps是可選的,若是不傳這個參數redux會簡單把dispatch做爲屬性注入給組件,能夠手動當作store.dispatch使用。這也是爲何要科裏化的緣由。
--------------------------------------------------------------------------------------------------------
因此這個過程應該是這樣的:
view ---> action ---> reducer ---> store(state) ---> view
若是放入一個web app中,
都是函數式編程裏的設計模式的東西,只是換了個馬甲。
先了解下MVP模式:[Android Module] 03 - Software Design and Architecture
Presenter的角色就是:從View收集loadData這樣的請求,而後交由Model的一個具體對應的函數去執行。
React 組件 API。咱們將講解如下7個方法:
例子:設置狀態:setState
<body> <div id="message" align="center"></div> <script type="text/babel"> var Counter = React.createClass({
getInitialState: function () { return { clickCount: 0 }; },
handleClick: function () { this.setState( function(state) { return {clickCount: state.clickCount + 1}; } ); },
render: function () { return (<h2 onClick={this.handleClick}>點我!點擊次數爲: {this.state.clickCount}</h2>); }
});
ReactDOM.render( <Counter />, document.getElementById('message') );
</script> </body>
組件的生命週期可分紅三個狀態:
生命週期的方法:
componentWillMount 在渲染前調用,在客戶端也在服務端。
componentDidMount : 在第一次渲染後調用,只在客戶端。以後組件已經生成了對應的DOM結構,能夠經過this.getDOMNode()來進行訪問。 若是你想和其餘JavaScript框架一塊兒使用,能夠在這個方法中調用setTimeout, setInterval或者發送AJAX請求等操做(防止異部操做阻塞UI)。
componentWillReceiveProps 在組件接收到一個新的 prop (更新後)時被調用。這個方法在初始化render時不會被調用。
shouldComponentUpdate 返回一個布爾值。在組件接收到新的props或者state時被調用。在初始化時或者使用forceUpdate時不被調用。 能夠在你確認不須要更新組件時使用。
componentWillUpdate在組件接收到新的props或者state但尚未render時被調用。在初始化時不會被調用。
componentDidUpdate 在組件完成更新後當即調用。在初始化時不會被調用。
componentWillUnmount在組件從 DOM 中移除的時候馬上被調用。
1. 在輸入框值發生變化時咱們能夠更新 state。
var HelloMessage = React.createClass({
getInitialState: function() { return {value: 'Hello Runoob!'}; },
------------------------------------------------------ handleChange: function(event) { this.setState({value: event.target.value}); },
------------------------------------------------------ render: function() { var value = this.state.value; return <div> <input type="text" value={value} onChange={this.handleChange} /> // 監聽到value發生了變化,觸發handleChange. <h4>{value}</h4> </div>; } });
ReactDOM.render( <HelloMessage />, document.getElementById('example') );
2. 在子組件上使用表單。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>菜鳥教程 React 實例</title> <script src="https://cdn.bootcss.com/react/15.4.2/react.min.js"></script> <script src="https://cdn.bootcss.com/react/15.4.2/react-dom.min.js"></script> <script src="https://cdn.bootcss.com/babel-standalone/6.22.1/babel.min.js"></script> </head> <body> <div id="example"></div>
<script type="text/babel">
var Content = React.createClass({ render: function() { return <div> <input type="text" value={this.props.myDataProp} onChange={this.props.updateStateProp} /> <h4>{this.props.myDataProp}</h4> </div>; } });
-- 以上是子組件 --
======================================================================
-- 如下是父組件 --
var HelloMessage = React.createClass({
getInitialState: function() { return {value: 'Hello Runoob!'}; },
---------------------------------------------------------------------- handleChange: function(event) { this.setState({value: event.target.value}); },
---------------------------------------------------------------------- render: function() { var value = this.state.value; return <div> <Content myDataProp = {value} updateStateProp = {this.handleChange}>
</Content> </div>; } });
ReactDOM.render( <HelloMessage />, document.getElementById('example') );
</script> </body> </html>
1. 經過 onClick 事件來修改數據:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>菜鳥教程 React 實例</title> <script src="https://cdn.bootcss.com/react/15.4.2/react.min.js"></script> <script src="https://cdn.bootcss.com/react/15.4.2/react-dom.min.js"></script> <script src="https://cdn.bootcss.com/babel-standalone/6.22.1/babel.min.js"></script> </head>
<body> <div id="example"></div> <script type="text/babel">
------------------------------------------------------------------- var HelloMessage = React.createClass({ getInitialState: function() { return {value: 'Hello Runoob!'}; },
------------------------------------------------------------------- handleChange: function(event) { this.setState({value: '菜鳥教程'}) },
-------------------------------------------------------------------- render: function() { var value = this.state.value; return <div> <button onClick={this.handleChange}>點我</button> <h4>{value}</h4> </div>; } }); ReactDOM.render( <HelloMessage />, document.getElementById('example') ); </script> </body>
</html>
2. 從子組件中更新父組件的 state 時,你須要在父組件經過建立事件句柄 (handleChange) ,並做爲 prop (updateStateProp) 傳遞到你的子組件上。
<body>
<div id="example"></div> <script type="text/babel">
var Content = React.createClass({ render: function() { return <div> <button onClick = {this.props.updateStateProp}>點我</button> <h4>{this.props.myDataProp}</h4> </div> } });
-- 以上是子組件 --
=======================================================================================
-- 如下是父組件 --
var HelloMessage = React.createClass({ getInitialState: function() { return {value: 'Hello Runoob!'}; },
--------------------------------------------------------------------------------------- handleChange: function(event) { this.setState({value: '菜鳥教程'}) },
--------------------------------------------------------------------------------------- render: function() { var value = this.state.value; return <div> <Content myDataProp = {value} updateStateProp = {this.handleChange}></Content> </div>; } });
ReactDOM.render( <HelloMessage />, document.getElementById('example') ); </script> </body>