不寫性能優化的時候
const Child = (props) => {console.log(child function is recalled)// count1改變時多次執行return (<div><h1>{ props.count2}</h1></div>)
}
function app () {const [count1.setCount1] = useState(0)const [count2.setCount2] = useState(0)return (<div><h1>count1:{count1}</h1><button onClick={()=> setCount1(count1+1)}>+</button><Child count2={count2}/><button onClick={()=> setCount2(count2+1)}>+</button></div>)
}
root.render(<App />)
memo
const Child = memo((props) => {console.log(child function is recalled)// count1改變時不會多次執行return (<div><h1>{ props.count2}</h1></div>)
})
/*** * 函數組件最大的弱點:渲染執行* 當一個組建的狀態發生了改變的時候 相關視圖是必然要更新的* 函數組件在視圖更新的需求來臨的時候 函數是必然要執行的*/
function app () {const [count1.setCount1] = useState(0)const [count2.setCount2] = useState(0)return (<div><h1>count1:{count1}</h1><button onClick={()=> setCount1(count1+1)}>+</button><Child count2={count2}/><button onClick={()=> setCount2(count2+1)}>+</button></div>)
}
root.render(<App />)
useMemo
/*** memo會對引用進行比較,這種比較時淺比較,* 如果更新了一個新的引用,那么child就會被執行* count1更新 app重新執行 child 必然要重新賦值一個新引用*/
const Child = memo((props) => {console.log(child function is recalled)// count1改變時會多次執行return (<div><h1>{ props.childData.count2}</h1></div>)
})
function app () {const [count1.setCount1] = useState(0)const [count2.setCount2] = useState(0)const childData = {count2}return (<div><h1>count1:{count1}</h1><button onClick={()=> setCount1(count1+1)}>+</button><Child childData={childData}/><button onClick={()=> setCount2(count2+1)}>+</button></div>)
}
const Child = memo(props) => {console.log(child function is recalled)// count1改變時不會多次執行return (<div><h1>{ props.childData.count2}</h1></div>)
}
function app () {const [count1.setCount1] = useState(0)const [count2.setCount2] = useState(0)// const childData = {// count2// }// 只有依賴變了才會返回新的引用// 不是為了子組件/返回常量的時候,不需要使用useMemoconst childData = useMemo(() => ({count2}),[count2])/*** const newCount1 = useMemo(()=>count1*2,[count1])* 類似vue計算屬性 可以使用useMemo*/return (<div><h1>count1:{count1}</h1><button onClick={()=> setCount1(count1+1)}>+</button><Child childData={childData}/><button onClick={()=> setCount2(count2+1)}>+</button></div>)
}
useCallback
const Child = memo(props) => {console.log(child function is recalled)// count1改變時會多次執行return (<div><h1>{props.childData.count2}</h1><button onClick={props.setCount2}>+</button></div>)
}function app () {const [count1.setCount1] = useState(0)const [count2.setCount2] = useState(0)const cbSetCount2 = () => {() => setCount2(count2 + 1);}return (<div><h1>count1:{count1}</h1><button onClick={()=> setCount1(count1+1)}>+</button><Child childData={childData} setCount2={cbSetCount2 } /></div>)
}
const Child = memo(props) => {console.log(child function is recalled)// count1改變時不會多次執行return (<div><h1>{props.childData.count2}</h1><button onClick={props.setCount2}>+</button></div>)
}function app () {const [count1.setCount1] = useState(0)const [count2.setCount2] = useState(0)// 函數是靜態的 一般情況下是不會增加依賴的 除非是用戶配置 比如面向對象 在this上增加函數 prototype上增加函數const cbSetCount2 = useCallback(() => {() => setCount2(count2 + 1);},[])return (<div><h1>count1:{count1}</h1><button onClick={()=> setCount1(count1+1)}>+</button><Child childData={childData} setCount2={cbSetCount2 } /></div>)
}
memo手寫
const { component } = React;
export default class PureComponent extends Component {// 如果你需要自定義shouldComponentUpdate 那么就不能繼承pureComponent(pureComponent字段該函數)// 該函數無法鑒定動態的變化shouldComponentUpdate (nextProps, nextState) { return !shallowEqual(this.props,nextProps) ||!shallowEqual(this.state,nextState)}
}function shallowEqual (o1, o2) {// 組件不能調用render函數if (o1 === o2) return true;if (typeof o1 !== 'object' || o1 === null || typeof o2 !== 'object' || o2 === null) {return false;}const k1 = Object.keys(o1);const k2 = Object.keys(o2);if (k1.length !== k2.length) return false;for (const k of k1) {if (!o2.hasOwnProperty(k) || o1[k] !== o2[k]) {return false;}}return true;
}
export function memo (Fc) {return class extends PureComponent{return () {return Fc(this.props)}}
}
useMemo
function useMemo(cb,depArr){if (memoArr[memoIndex]) {const [_memo, _dep] = memoArr[memoIndex]const isFullySame = depArr.every((dep, index) => dep === _dep[index]);if (isFullySame) { memoIndex++;return _memo;} else {return setNewMemo(cb,depArr)}} else {return setNewMemo(cb,depArr)}function setNewMemo (cb, depArr) {const memo = cb();memoArr[memoIndex++] = [memo, depArr]return memo;}
}useCallback
function useCallback(cb,depArr){if (callbackArr[callbackIndex]) {const [_callback, _dep] = callbackArr[callbackIndex]const isFullySame = depArr.every((dep, index) => dep === _dep[index]);if (isFullySame) { callbackIndex++;return _callback;} else {return setNewCallback(cb,depArr)}} else {return setNewCallback(cb,depArr)}function setNewCallback (cb, depArr) {callbackArr[callbackIndex++] = [cb,depArr]}
}