constructor(props) { super(props); this.state = { count: 0 }; this.increase = this.increase.bind(this); } increase() { this.setState({ count: this.state.count + 1 }); // 第一次輸出 console.log('第一次輸出:', this.state.count); this.setState({ count: this.state.count + 1 }); // 第二次輸出 console.log('第二次輸出:', this.state.count); setTimeout(() => { this.setState({ count: this.state.count + 1 }); // 第三次輸出 console.log('第三次輸出:', this.state.count); this.setState({ count: this.state.count + 1 }); // 第四次輸出 console.log('第四次輸出:', this.state.count); }, 1000) }
不知道大家內心是否有上面代碼的答案了呢?不錯,正確輸出是:0 0 2 3
。那可能剛開始學React的童鞋就會問了?爲何前面都是0,後面的正常加了了?那這個setState到底是同步的仍是異步的了?那麼,接下來就爲你解答心中的疑惑=。=vue
前兩次的setState,不會當即改變React組件中的state的值,這兩次輸出的都是0;合併更新,將屢次的setState合併成一次,不引起重複渲染異步
setTimeout 同步更新,引起兩次渲染。函數
其實,這只是React的障眼法。優化
setState是同步執行的!可是state並不必定會同步更新(異步更新合同步更新都存在)
那setState
究竟幹了什麼了?this
setState()
中有個特別重要的布爾屬性isBatchingUpdates
(默認爲false),它決定了state
是同步更新仍是異步更新。spa
setState
只在合成事件何鉤子函數中是「異步更新」的。異步更新的背後,是同步代碼處理(「合成事件何鉤子函數」的調用在「更新」前)。code
異步是爲了實現批量更新的手段,也是React新能優化的一種方式。component
要知道setState
本質是經過一個隊列機制
實現state
更新的。執行setState
時,會將須要更新的state合併
後放入狀態隊列,而不會馬上更新state
,隊列機制能夠批量更新state
。若是不經過setState
而直接修改this.state
,那麼這個state
不會放入狀態隊列中,下次調用setState
時,狀態隊列新進行合併時,會忽略以前直接被修改的state
,這樣咱們就沒法合併了,並且實際也沒有把你想要的state
更新上去。
固然了,咱們也是有辦法同步獲取state更新
後的值:server
setTimeout
等異步操做中調用setState
函數DOM
原生事件setState
回調函數setState
用法前兩個都是比較好理解,由於沒有前置的batchdUpdate
調用,因此isBatchingUpdates
爲false
。不會開啓批量更新模式。對象
後面兩個方法,是React自己提供的。要注意的是,setState
回調函數要在render函數
被從新執行後才執行。
// 回調函數 this.setState({ count: 1 }, () => { console.log(this.state.count) }) // 函數式 this.setState(prevState => { return { count: prevState.count + 1 } })
Vue
在監聽到數據變化後,會開啓一個隊列,並緩衝在同一事件循環中發生的全部數據變動(若是同一個watcher
被屢次觸發,只會被推入到隊列中一次)。而後,在下一次事件循環Tick/微任務
中,Vue刷新隊列執行實際工做。
vue批量更新體現爲:
Mutation Observer(變更觀察器
)是偵聽DOM
變更的接口。當DOM對象樹發生任何變更時,Mutation Observer會獲得通知。針對以上的知識點,咱們來看看下面這段代碼,看咱們是否理解setState:
class Demo extends React.Component { state = { count: 0, }; componentDidMount() { this.setState({ count: this.state.count + 1, }); console.log("console: " + this.state.count); this.setState({ count: this.state.count + 1 }, () => { console.log("console from callback: " + this.state.count); }); this.setState( (prevState) => { console.log("console from func: " + prevState.count); return { count: prevState.count + 1, }; }, () => { console.log("last console: " + this.state.count); } ); } render() { console.log("render: " + this.state.count); return <h4>test</h4>; } }
state
更新須要經過setState
,而不能直接操做state
。setState
更新,state
不會馬上生效。setState
不是同步一個個執行,會加入到一個隊列中,而後最後一塊兒執行,及批量更新。