本篇文章主要幫助你們瞭解下react
技術棧相關的概念,以及爲何咱們須要引入這些,他們能解決什麼問題。vue
vue1
沒有加入虛擬DOM
,作服務端渲染很難,因此vue2
引入了虛擬DOM
的機制,並且因爲vue2
的響應式原理,因此自然的就比react
的性能好,react
的更新是經過頂層組件的state
變化觸發整個組件的從新渲染,而vue2
因爲其是經過getter/setter
來進行數據管理,因此能夠準確的定位到須要從新渲染的節點,避免了無效的re-render
react
native
端支持很差,weex
很厲害可是目前只有阿里用於生產環境,而react native
有着大量的成熟案例,如手機QQ編程
小,用在移動端很合適redux
小的缺點不少,可是比較好克服,最大的缺點仍是在於native
端,若是想用riot
實現native
端的話,須要造輪子,寫就是本身寫一套 Native Bridge
,來進行js
與Objective C
通訊,難度太大,須要引入js引擎
等高大上的東西(Native Bridge
基本上能夠理解爲一個瀏覽器內核,並且確定是C++
寫)。瀏覽器
一個線程爲js
引擎,執行打包好的js
,主線程負責UI
繪製,js
須要繪製UI
時會向主線程發出一個命令,主線程接收到命令後執行相應的繪製邏輯,Objective C
執行的結果會通過層層回調經過js
引擎傳回給js
。緩存
利用redux
作數據管理的話,redux
的store
會被放置到最頂層組件的state
中,也就是react-redux
爲咱們提供的Provider
組件。這樣就是意味着每次store
發生變化就會從新渲染整個應用,也就是觸發全部組件的render
方法,每次都會觸發diff
,可是這種大可能是無心義的,這也是產生性能瓶頸的地方:服務器
以下面代碼:weex
render() { const { child1Data, child2Data } = this.props.store; return ( <div> <Child1 data="child1Data" /> <Child2 data="child2Data" /> </div> ); }
假如只有child1Data
發生變化,而child2Data
並無發生變化,理論上來講咱們只想觸發Child1
的render
,但事實上咱們同時會觸發Child2
的render
,此次顯然是無心義的,因此須要來解決這個問題。數據結構
react
的生命週期函數中有一個shouldComponentUpdate
,根據其返回的值來決定是否須要來觸發組件的render
,shouldComponentUpdate
默認返回的是true
,也就是不管什麼狀況都會觸發render
,purerender
改善的就是這個生命週期,根據傳入的state
和props
來進行簡單的判斷,從而決定是否須要進行render
,爲何說只是進行了簡單的判斷,來看其判斷部分代碼:
(注:利用connect將組件與redux
關聯起來的容器不須要加purerender
,由於這個工做react-redux
已經替咱們作好了)框架
// is能夠理解爲Object.is() function shallowEqual(objA, objB) { if (is(objA, objB)) { return true; } // 非引用類型,且不相等直接返回 if (typeof objA !== 'object' || objA === null || typeof objB !== 'object' || objB === null) { return false; } const keysA = Object.keys(objA), keysB = Object.keys(objB); if (keysA.length !== keysB.length) { return false; } // 問題所在,僅僅是比較了第一層,假設引用沒變,不會觸發更新 for (let i = 0; i < keysA.length; i++) { if ( !hasOwnProperty.call(objB, keysA[i]) || !is(objA[keysA[i]], objB[keysB][i]) ) { return false; } } return true; }
注意:使用react
時必定注意不要在render
函數中進行函數的bind
,由於這樣每次props
中會有屬性的引用改變,必定會觸發更新
對於通常的狀況來講purerender
已經足夠,能夠減小一些re-render
,可是不是很完全,好比:
狀況一:
// 以前的數據: let person = { name: 'zp1996', age: 21 }; // 改變引用 person = { name: 'zp1996', age: 21 };
(注:上面場景我作了下簡化,真實中多是從服務器端得到的數據,好比帖子列表這種)
明顯的引用發生了改變,因此會觸發re-render
,可是明顯的是個人數據徹底沒有變化,根本不用進行diff
。
狀況二:
const data = { person: { students: [{ name: 'zp1996', age: 21 }] } }; // 加入一個新的學生,數據結構會變成這樣 { person: { students: [{ name: 'zp1996', age: 20 }, { name: 'zpy', age: 21 }] } }
假如是這種狀況,引用根本沒有發生變化,因此就不會觸發re-render
,每次改變一個小的地方,就須要將整個的數據從新生成一個,這樣形成了內存的沒必要要的浪費。
很容易想到的是在shouldComponentUpdate
中進行深度比較,用遞歸的方式來進行比較,這樣的代價一樣很大,並非一個有效的解決方案。爲了解決這個問題,須要引入另外一個庫——immutable
,其思想是強調不可變數據,一個Immutable Data
的建立就是一個不可變的,須要變化時不是利用深拷貝,而是僅僅改變這個變化的節點和其父節點,其他節點還是共享內存。一樣的這樣的一個強大的框架也是很是大,壓縮事後仍然有50k
。
咱們但願的是reducer
中保持簡單,從服務端請求回來的數據直接存在store
中,看個例子:
// 從服務端拉回來的數據 { students: { 'id_111': { name: 'zp1996', age: 21 } } } // 最終組件但願咱們傳入這樣的數據 { students: [{ name: 'zp1996', age: 21, id: 'id_111' }] } // 通常會在connect中對數據進行整理 const mapStateToProp = state => { const { students } = state, res = []; for (let key in students) { if (students.hasOwnProperty(key)) { let obj = students[key]; obj[id] = key; res.push(obj); } } return res; }; @connect( mapStateToProp, // 被叫作selector );
每次store
變化後,也就是執行一次dispatch
以後都會執行利用subscribe
方法註冊的回調(註冊的回調就是connect
的第一個參數,也就是selector
),這樣就意味着,儘管students
並無發生變化仍是會觸發一次數據結構的重整,這種顯然是一種浪費,因此這個過程也須要優化:
redux
強調的是函數式編程,對於函數式編程來講有一個很明顯的特色就是易於緩存,對於一個函數而言,給定相同的輸入確定會獲得相同的輸出,而selector
也所有爲純函數,同時connect
的mapStateToProp
參數也支持返回一個函數。reselect
庫就是這個思想,先來看看基本用法:
// createSelector的最後一個參數做爲計算函數 const state = { num: 10 }, selector = state => state.num, reSelector = createSelector( selector, a => { console.log('被調用了') return a * a; } ); console.log(reSelector(state)); // 第一次計算 console.log(reSelector(state)); // 拿緩存 console.log(reSelector(state)); // 拿緩存 state.num = 100; console.log(reSelector(state)); // 值發生改變,計算 console.log(reSelector(state)); // 拿緩存 console.log(reSelector(state)); // 拿緩存
實現也是很是簡單,就是對傳入的參數進行判斷,若是與以前同樣則直接返回結果
function defaultMemoize(func, equalityCheck = defaultEqualityCheck) { let lastArgs = null, lastResult = null; const isEqualToLastArg = (value, index) => equalityCheck(value, lastArgs[index]); return (...args) => { // 檢測輸入是否相等,不等或者第一次執行的話執行函數,反之拿之間的結果 if ( lastArgs === null || lastArgs.length !== args.length || !args.every(isEqualToLastArg) ) { lastResult = func(...args); } lastArgs = args; return lastResult; }; }
redux
會引入不少的概念,同時代碼量也會不少,而mobx
要更爲簡單。mobx
給個人感受就像是把vue
的響應式數據那一套給拿了出來,給了咱們極大的自由度,能夠利用OOP
那一套來創建模型,而用redux
必須利用redux
的那些套路寫代碼;同時在性能上也會有一些提高。可是同時也會帶來很大的問題:state
滿天飛是避免不了的,可是提供了一個strict
模式,要求數據必須利用action
來進行更改,可是我用了以後發現並無什麼做用,並且組件外是能夠隨意更改數據的。 還有一個小問題就是熱更新,根本沒有找到熱更新的解決方案,每次還得手動刷新頁面。
redux
還有一塊很重要的部分,那就是異步處理,目前本人只用過redux-thunk
,因此關於這個方面沒(水)有(平)講(不)到(夠),同時哪裏有錯誤也請你們指出。