前言
傳統(tǒng)的Diff算法O(N3),React Diff基于三大前提將復(fù)雜度降為O(N)
1.tree diff十嘿,跨層dom操作比較少吨悍,結(jié)構(gòu)不同直接銷毀
2.component diff婉烟,相同類型節(jié)點(diǎn)有相同樹形結(jié)構(gòu)砌些,只做更新,不同類型直接銷毀
3.element diff朵耕,同層子元素根據(jù)唯一標(biāo)識(shí)做區(qū)分
一.代碼優(yōu)化
1.key
設(shè)置唯一穩(wěn)定的key
render() {
return (
<div>
this.state.user.map(item => <div key={item.id}>{item.name}</div>)
</div>
)
}
2.節(jié)點(diǎn)內(nèi)容
節(jié)點(diǎn)類型分兩大類炫隶,一類是DOM元素類型(如div,span),另一類是React組件
Dom元素比較屬性和內(nèi)容
<div style={{color: 'red'}} onClick={() =>this.handleClick()}>變化前</div>
...
// 變化后屬性和內(nèi)容都變化了
<div style={{color: 'red'}} onClick={() =>this.handleClick()}>變化后</div>
注意引用類型的比較
{color: 'red'} === {color: 'red'} // false
var fun1 = () => {}
var fun2 = () => {}
fun1 === fun2 // false
優(yōu)化后阎曹,屬性指向都不變
const style = {color: 'red'}
...
constructor(props) {
super(props)
this.handleClick = this.handleClick.bind(this)
}
...
render() {
return {
<div style={style} onClick={this.handleClick}>更改后</div>
}
}
redux版本優(yōu)化前伪阶,
<TodoItem
key={item.id}
onRemove={() => onRemoveTodo(item.id)}
...
...
redux版本優(yōu)化后
// 方法1
<TodoItem
key={item.id}
onRemove={onRemoveTodo}
id={item.id}
...
>
...
const mapDispatchToProps = (dispatch,ownProps) => ({
onRemoveTodo: ownProps.onRemoveTodo(ownProps.id)),
})
// 方法2
<TodoItem
key={item.id}
id={item.id}
...
>
...
const mapDispatchToProps = (dispatch,ownProps) => ({
onRemoveTodo: () => dispatch(onRemoveTodo(ownProps.id)),
})
3.shouldComponentUpdate(nextProps,nextState)
為了避免浪費(fèi)多余的渲染,return false可以阻止組件的更新
shouldComponentUpdate(nextProps,nextState) {
return nextProps.isChange !== this.props.isChange // true or false
}
PureComponent類內(nèi)部也是用了shouldComponentUpdate
// PureComponent內(nèi)部
if (this._compositeType === CompositeTypes.PureClass) {
shouldUpdate = !shallowEqual(prevProps, nextProps)
|| !shallowEqual(inst.state, nextStat e);
}
return shouldUpdate;
shallowEqual(淺比較)芬膝,只做簡(jiǎn)單類型的判斷望门。
state = {
arr = [1,2,3,4,5]
}
shouldComponentUpdate(nextProps,nextState) {
return nextState.arr !== this.state.nextState // false
}
handleClick = () => {
const { arr } = this.state
arr.push(6)
this.setState({
arr,
})
}
render () {
...
}
解決方法deepEqual,但如果變量嵌套深會(huì)對(duì)性能有消耗
二.工具使用
1.immutable(一種不可更改的數(shù)據(jù))
網(wǎng)上一般寫法
import { is } from 'immutable'
...
shouldComponentUpdate = (nextProps = {} , nextState = {}) => {
if(Object.keys(this.props).length !== Object.keys(nextProps).length ||
Object.keys(this.state).length !== Object.keys(nextState).length
) {
return true
}
for(const key in nextProps) {
if(this.props[key] !== nextProps[key] || !is(this.props[key],nextProps[key])) {
return true
}
}
for(const key in nextState) {
if(this.state[key] !== nextState[key] || !is(this.state[key],nextState[key])) {
return true
}
}
return false
}
...
我項(xiàng)目中優(yōu)化前后對(duì)比
shouldComponentUpdate = (nextProps = {} , nextState = {}) => {
if(!is(nextState,this.state)) {
return true
}
return false
}
使用了結(jié)構(gòu)共享锰霜,避免deepCopy筹误,可節(jié)省內(nèi)存
2.reselect
一種選擇器中間件,認(rèn)為輸入?yún)?shù)state相同癣缅,就沒必要進(jìn)行計(jì)算厨剪,直接抽取以往的值
const mapState = (state)=>({
todos:state.todos,
filter:state.filter,
visibleTodos:getVisibleTodos(state.todos,state.filter)
});
//selector.js
export const todosSelector = (state) => state.todos;
export const filterSelector = (state) => state.filter;
export const visibleTodosSelector = createSelector(
[todosSelector,filterSelector],
(todos,filter)=>{return getVisibleTodos(todos,filter)} //這里假設(shè)已經(jīng)定義一個(gè)getVisibleTodos函數(shù)用來返回要顯示哪些todo項(xiàng)
);
//container.js
import {visibleTodosSelector}
const mapState = (state)=>({
todos:visibleTodosSelector(state)
});
3.其他
少用{...props}
適當(dāng)拆分組件
壓縮哄酝,合并,commonChunksPulgin(webpack4里面移除了commonChunksPulgin插件祷膳,放在了config.optimization里面)
css預(yù)處理語言寫法陶衅,防止多余編譯
三.Fiber架構(gòu)遷移
以往的react渲染是一氣呵成,不能打斷直晨,同步渲染計(jì)算大的話容易阻塞UI線程搀军。
react16后提出Fiber,使react從Stack reconciler轉(zhuǎn)變?yōu)閒iber reconciler
將渲染分割成多個(gè)事務(wù)勇皇,使得以往的棧結(jié)構(gòu)可以定制優(yōu)先級(jí)罩句,暫停,復(fù)用敛摘,其中第一個(gè)階段是可以隨時(shí)被打斷的階段门烂,這使得某部分舊的生命周期函數(shù)造成不安全的危害
componentWillMount
componentWillReceiveProps
componentWillUpdate
用其它生命周期函數(shù)再結(jié)合兩個(gè)新的生命周期函數(shù),足以替代它們的業(yè)務(wù)場(chǎng)景
getDerivedStateFromProps(nextProps, prevState)
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.selectCodeId !== prevState.codeId) {
return {
codeId: nextProps.selectCodeId,
first: false,
};
}
return null;
}
getDerivedStateFromProps沒有附加渲染的情況下更新狀態(tài)的唯一方法兄淫,可以用來替代componentWillReceiveProps
getSnapshotBeforeUpdate(prevProps, prevState)
export default class ScrollingList extends Component {
constructor(props) {
super(props)
this.scrollRef = null
}
getSnapshotBeforeUpdate(prevProps, prevState) {
if (prevProps.list.length < this.props.list.length) {
return (
this.scrollRef.scrollHeight - this.scrollRef.scrollTop
);
}
return null;
}
componentDidUpdate(prevProps, prevState, snapshot) {
if (snapshot !== null) {
this.scrollRef.scrollTop =
this.scrollRef.scrollHeight - snapshot;
}
}
render() {
return (
....
);
}
}