首先須要去理解純函數的概念:相同的輸入,擁有有相同輸出,沒有任何反作用。同理: 咱們的函數式組件也不會去根據組件的狀態(state)的不一樣輸入,不會擁有不一樣的表現形式。(與state無關,取決於props與context)。javascript
// 引入package等省略,這是一個全局公共的Loading。
const Loading = ({ size = '', loading = false, inner = false }) => {
if (loading) {
return inner? (
// 沒有size屬性,默認用自定義的大小
<div className={classNames(
styles['inner-loading-container'],
{[styles['custom-size']]: !size}
)}>
<Spin size={size || 'small'} className={styles.loading} /> </div>
): (
createPortal(
<div className={styles['loading-container']}> <Spin className={styles.loading} /> </div>, document.querySelector('body'), ) ); } return null; }; 複製代碼
正常狀況下,父組件每次有state或者props改變,子組件都會從新渲染。可是若是咱們在shouldComponentUpdate階段判斷新舊屬性和狀態是否相等,是否須要從新渲染子組件。減小render()方法的觸發,節省了在虛擬DOM生成與對比的過程,性能獲得提高。前端
這個比較的過程是一個淺比較,沒法判斷複雜數據類型(引用類型)的變化。java
即便是淺比較,一樣是須要消耗性能。react
若是不是immutale數據的話你可能會出現問題(同一引用類型改變致使不更新渲染),一旦深層次的結構出現問題,你定位問題可能須要好久。git
這不足以成爲一個正常應用的性能瓶頸,當你發現已經到不優化不可的地步,那確定不是PureComponent致使的,優化的優點也就不那麼明顯了。github
// 淺比較shallowEqual的源碼
const hasOwn = Object.prototype.hasOwnProperty
// 這個函數其實是Object.is()的polyfill
function is(x, y) {
if (x === y) {
return x !== 0 || y !== 0 || 1 / x === 1 / y
} else {
return x !== x && y !== y
}
}
export default function shallowEqual(objA, objB) {
// 首先對基本數據類型的比較
if (is(objA, objB)) return true
// 因爲Obejct.is()能夠對基本數據類型作一個精確的比較, 因此若是不等
// 只有一種狀況是誤判的,那就是object,因此在判斷兩個對象都不是object以後,就能夠返回false了
if (typeof objA !== 'object' || objA === null ||
typeof objB !== 'object' || objB === null) {
return false
}
// 過濾掉基本數據類型以後,就是對對象的比較了
// 首先拿出key值,對key的長度進行對比
const keysA = Object.keys(objA)
const keysB = Object.keys(objB)
// 長度不等直接返回false
if (keysA.length !== keysB.length) return false
for (let i = 0; i < keysA.length; i++) {
// key值相等的時候
// 借用原型鏈上真正的 hasOwnProperty 方法,判斷ObjB裏面是否有A的key的key值
// 屬性的順序不影響結果也就是{name:'daisy', age:'24'} 跟{age:'24',name:'daisy' }是同樣的
// 最後,對對象的value進行一個基本數據類型的比較,返回結果
if (!hasOwn.call(objB, keysA[i]) ||
!is(objA[keysA[i]], objB[keysA[i]])) {
return false
}
}
return true
}
複製代碼
高階組件的概念其實是來源於咱們的高階函數:接收函數做爲輸入,或者輸出另外一個函數的一類函數,被稱做高階函數。高階組件: 接受React組件做爲輸入,輸出一個新的React組件的組件。高階組件和裝飾器是一個模式,高階組件能夠看成裝飾器使用。redux
react-redux中的connect(mapStateToProps, mapDispatchToProps, mergeProps,options): 此方法會將react組件鏈接到redux的store。connect經過函數參數mapStateToProps
,從全局store中取出當前組件須要的state,並把state轉化成當前組件的props;同時經過函數參數mapDispatchToProps
,把當前組件用到的Redux的action creator,以props的方式傳遞給當前組件。connect
並不會修改傳遞進去的組件的定義,而是它會返回一個新的組件。bash
react-router中的withRouter: 經過withRouter
包裝的組件,咱們能夠在props中訪問到location, router
等對象,這正是withRouter
經過高階組件的方式傳遞過來的。react-router
UI框架中的受控組件:react-hoc-example框架
不要在組件的render方法中使用高階組件,儘可能也不要在組件的其餘生命週期方法中使用高階組件。由於高階組件每次都會返回一個新的組件,在render中使用會致使每次渲染出來的組件都不相等(===
),因而每次render,組件都會卸載(unmount),而後從新掛載(mount),既影響了效率,又丟失了組件及其子組件的狀態。高階組件最適合使用的地方是在組件定義的外部,這樣就不會受到組件生命週期的影響了。
若是須要使用被包裝組件的靜態方法,那麼必須手動拷貝這些靜態方法。由於高階組件返回的新組件,是不包含被包裝組件的靜態方法。hoist-non-react-statics能夠幫助咱們方便的拷貝組件全部的自定義靜態方法。
Refs不會被傳遞給被包裝組件。儘管在定義高階組件時,咱們會把全部的屬性都傳遞給被包裝組件,可是ref
並不會傳遞給被包裝組件,由於ref
根本不屬於React組件的屬性。若是你在高階組件的返回組件中定義了ref
,那麼它指向的是這個返回的新組件,而不是內部被包裝的組件。若是你但願獲取被包裝組件的引用,你能夠把ref
的回調函數定義成一個普通屬性(給它一個ref之外的名字)。
function FocusInput({ inputRef, ...rest }) {
return <input ref={inputRef} {...rest} />;
}
//enhance 是一個高階組件
const EnhanceInput = enhance(FocusInput);
// 在一個組件的render方法中...
return (<EnhanceInput
inputRef={(input) => {
this.input = input
}
}>)
// 讓FocusInput自動獲取焦點
this.input.focus();
複製代碼
類組件使用PureComponent或者shouldComponentUpdate可以優化props值不變時候的渲染性能(默認是shallowEqual)。如今, 你能夠經過使用React.memo對function組件進行一樣的優化。固然你也能夠在方法的第二個參數自定義compare方法。實際意義是:函數式組件也有「shouldComponentUpdate」生命週期了
const MyComponent = React.memo(function MyComponent(props) {
/* 只在props更改的時候纔會從新渲染 */
});
function areEqual(prevProps, nextProps) {
/*
return true if passing nextProps to render would return
the same result as passing prevProps to render,
otherwise return false
*/
}
function MyComponent(props) {
/* render using props */
}
export default React.memo(MyComponent, areEqual);
複製代碼
類聲明和類表達式的主體以 嚴格模式 執行,主要包括構造函數、靜態方法和原型方法。Getter 和 setter 函數也在嚴格模式下執行。不是React的緣由,這是JavaScript中原本就有的。若是你傳遞一個函數名給一個變量,而後經過在變量後加括號()來調用這個方法,此時方法內部的this的指向就會丟失.
箭頭函數沒有 this,因此須要經過查找做用域鏈來肯定 this 的值。這就意味着若是箭頭函數被非箭頭函數包含,this 綁定的就是最近一層非箭頭函數的 this。this 是有詞法約束力的。這意味它可使用封閉的函數上下文或者全局上下文做爲 this 的值。