摘要:是事件回調(diào),有時(shí)機(jī)執(zhí)行邏輯,所以為,,是合并的,結(jié)束之后被重新設(shè)置為。沒(méi)有控制權(quán)的情況有很多回調(diào),網(wǎng)絡(luò)回調(diào)等等。的說(shuō)明從函數(shù)名上理解強(qiáng)制更新。所以可以簡(jiǎn)單的理解為,只不過(guò)這個(gè)是不調(diào)用自己的聲明周期的。
setState同步異步問(wèn)題,React批量更新一直是一個(gè)比較模糊的問(wèn)題,本文希望從框架設(shè)計(jì)的角度說(shuō)明一下這個(gè)問(wèn)題。
React有個(gè)UI = f(data) 公式:UI是由data推導(dǎo)出來(lái)的,所以在寫應(yīng)用的時(shí)候,我們只需要關(guān)心數(shù)據(jù)的改變,只需data ---> data", 那么UI ---> UI",在這個(gè)過(guò)程中,我們其實(shí)并不關(guān)心UI是怎么變化到UI‘的(即DOM的變化),這部分工作是React替我們處理了。
那么React是如何知道當(dāng)數(shù)據(jù)變化的時(shí)候,需要修改哪些DOM的呢?最簡(jiǎn)單暴力的是,每次都重新構(gòu)建整個(gè)DOM樹(shù)。實(shí)際上,React使用的是一種叫virtual-dom的技術(shù):用JS對(duì)象來(lái)表示DOM結(jié)構(gòu),通過(guò)比較前后JS對(duì)象的差異,來(lái)獲得DOM樹(shù)的增量修改。virtual-dom通過(guò)暴力的js計(jì)算,大大減少了DOM操作,讓UI = f(data)這種模型性能不是那么的慢,當(dāng)然你用原生JS/jquery直接操作DOM永遠(yuǎn)是最快的。
setState 批量更新除了virtual-dom的優(yōu)化,減少數(shù)據(jù)更新的頻率是另外一種手段,也就是React的批量更新。 比如:
g() { this.setState({ age: 18 }) this.setState({ color: "black‘ }) } f() { this.setState({ name: "yank" }) this.g() }
會(huì)被React合成為一次setState調(diào)用
f() { this.setState({ name: "yank", age: 18, color: "black" }) }
我們通過(guò)偽碼大概看一下setState是如何合并的。
setState實(shí)現(xiàn)
setState(newState) { if (this.canMerge) { this.updateQueue.push(newState) return } // 下面是真正的更新: dom-diff, lifeCycle... ... }
然后f方法調(diào)用
g() { this.setState({ age: 18 }) this.setState({ color: "black‘ }) } f() { this.canMerge = true this.setState({ name: "yank" }) this.g() this.canMerge = false // 通過(guò)this.updateQueue合并出finalState const finalState = ... // 此時(shí)canMerge 已經(jīng)為false 故而走入時(shí)機(jī)更新邏輯 this.setState(finaleState) }
可以看出 setState首先會(huì)判斷是否可以合并,如果可以合并,就直接返回了。
不過(guò)有同學(xué)會(huì)問(wèn):在使用React的時(shí)候,我并沒(méi)有設(shè)置this.canMerge呀?我們的確沒(méi)有,是React隱式的幫我們?cè)O(shè)置了!事件處理函數(shù),聲明周期,這些函數(shù)的執(zhí)行是發(fā)生在React內(nèi)部的,React對(duì)它們有完全的控制權(quán)。
class A extends React.Component { componentDidMount() { console.log("...") } render() { return ({ console.log("hi") }}>} }
在執(zhí)行componentDidMount前后,React會(huì)執(zhí)行canMerge邏輯,事件處理函數(shù)也是一樣,React委托代理了所有的事件,在執(zhí)行你的處理函數(shù)函數(shù)之前,會(huì)執(zhí)行React邏輯,這樣React也是有時(shí)機(jī)執(zhí)行canMerge邏輯的。
批量更新是極好滴!我們當(dāng)然希望任何setState都可以被批量,關(guān)鍵點(diǎn)在于React是否有時(shí)機(jī)執(zhí)行canMerge邏輯,也就是React對(duì)目標(biāo)函數(shù)有沒(méi)有控制權(quán)。如果沒(méi)有控制權(quán),一旦setState提前返回了,就再也沒(méi)有機(jī)會(huì)應(yīng)用這次更新了。
class A extends React.Component { handleClick = () => { this.setState({x: 1}) this.setState({x: 2}) this.setState({x: 3}) setTimeout(() => { this.setState({x: 4}) this.setState({x: 5}) this.setState({x: 6}) }, 0) } render() { return ( } }
handleClick 是事件回調(diào),React有時(shí)機(jī)執(zhí)行canMerge邏輯,所以x為1,2,3是合并的,handleClick結(jié)束之后canMerge被重新設(shè)置為false。注意這里有一個(gè)setTimeout(fn, 0)。 這個(gè)fn會(huì)在handleClick之后調(diào)用,而React對(duì)setTimeout并沒(méi)有控制權(quán),React無(wú)法在setTimeout前后執(zhí)行canMerge邏輯,所以x為4,5,6是無(wú)法合并的,所以fn這里會(huì)存在3次dom-diff。React沒(méi)有控制權(quán)的情況有很多: Promise.then(fn), fetch回調(diào),xhr網(wǎng)絡(luò)回調(diào)等等。
unstable_batchedUpdates 手動(dòng)合并那x為4,5,6有辦法合并嗎?是可以的,需要用unstable_batchedUpdates這個(gè)API,如下:
class A extends React.Component { handleClick = () => { this.setState({x: 1}) this.setState({x: 2}) this.setState({x: 3}) setTimeout(() => { ReactDOM.unstable_batchedUpdates(() => { this.setState({x: 4}) this.setState({x: 5}) this.setState({x: 6}) }) }, 0) } render() { return ( } }
這個(gè)API,不用解釋太多,我們看一下它的偽碼就很清楚了
function unstable_batchedUpdates(fn) { this.canMerge = true fn() this.canMerge = false const finalState = ... //通過(guò)this.updateQueue合并出finalState this.setState(finaleState) }
so, unstable_batchedUpdates 里面的setState也是會(huì)合并的。
forceUpdate的說(shuō)明forceUpdate從函數(shù)名上理解:“強(qiáng)制更新”。 既然是“強(qiáng)制更新”有兩個(gè)問(wèn)題容易引起誤解:
forceUpdate 是同步的嗎?“強(qiáng)制”會(huì)保證調(diào)用然后直接dom-diff嗎?
“強(qiáng)制”更新整個(gè)組件樹(shù)嗎?包括自己,子孫后代組件嗎?
這兩個(gè)問(wèn)題官方文檔都沒(méi)有明確說(shuō)明。
class A extends React.Component{ handleClick = () => { this.forceUpdate() this.forceUpdate() this.forceUpdate() this.forceUpdate() } shouldComponentUpdate() { return false } render() { return () } }// 一個(gè)組件
對(duì)于第一個(gè)問(wèn)題:forceUpdate在批量與否的表現(xiàn)上,和setState是一樣的。在React有控制權(quán)的函數(shù)里,是批量的。
對(duì)于第二個(gè)問(wèn)題:forceUpdate只會(huì)強(qiáng)制本身組件的更新,即不調(diào)用“shouldComponentUpdate”直接更新,對(duì)于子孫后代組件還是要調(diào)用自己的“shouldComponentUpdate”來(lái)決定的。
所以forceUpdate 可以簡(jiǎn)單的理解為 this.setState({}),只不過(guò)這個(gè)setState 是不調(diào)用自己的“shouldComponentUpdate”聲明周期的。
Fiber 的想象顯示的讓開(kāi)發(fā)者調(diào)用unstable_batchedUpdates是不優(yōu)雅的,開(kāi)發(fā)者不應(yīng)該被框架的實(shí)現(xiàn)細(xì)節(jié)影響。但是正如前文所說(shuō),React沒(méi)有控制權(quán)的函數(shù),unstable_batchedUpdates好像是不可避免的。 不過(guò) React16.x的fiber架構(gòu),可能有所改變。我們看下fiber下的更新
setState(newState){ this.updateQueue.push(newState) requestIdleCallback(performWork) }
requestIdleCallback 會(huì)在瀏覽器空閑時(shí)期調(diào)用函數(shù),是一個(gè)低優(yōu)先級(jí)的函數(shù)。
現(xiàn)在我們?cè)倏紤]一下:
handleClick = () => { this.setState({x: 1}) this.setState({x: 2}) this.setState({x: 3}) setTimeout(() => { this.setState({x: 4}) this.setState({x: 5}) this.setState({x: 6}) }, 0) }
當(dāng)x為1,2,3,4,5,6時(shí) 都會(huì)進(jìn)入更新隊(duì)列,而當(dāng)瀏覽器空閑的時(shí)候requestIdleCallback會(huì)負(fù)責(zé)來(lái)執(zhí)行統(tǒng)一的更新。
由于fiber的調(diào)度比較復(fù)雜,這里只是簡(jiǎn)單的說(shuō)明,具體能不能合并,跟優(yōu)先級(jí)還有其他都有關(guān)系。不過(guò)fiber的架構(gòu)的確可以更加優(yōu)雅的實(shí)現(xiàn)批量更新,而且不需要開(kāi)發(fā)者顯示的調(diào)用unstable_batchedUpdates
廣告時(shí)間最后,廣告一下我們開(kāi)源的RN轉(zhuǎn)小程序引擎alita,alita區(qū)別于現(xiàn)有的社區(qū)編譯時(shí)方案,采用的是運(yùn)行時(shí)處理JSX的方式,詳見(jiàn)這篇文章。
所以alita內(nèi)置了一個(gè)mini-react,這個(gè)mini-react同樣提供了合成setState/forceUpdate更新的功能,并對(duì)外提供了unstable_batchedUpdates接口。如果你讀react源碼無(wú)從下手,可以看一下alita minil-react的實(shí)現(xiàn),這是一個(gè)適配小程序的react實(shí)現(xiàn), 且小,代碼在https://github.com/areslabs/alita/tree/master/packages/wx-react。
alita地址:https://github.com/areslabs/alita。 歡迎star & pr & issue
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/105505.html
摘要:會(huì)自動(dòng)觸發(fā)函數(shù)內(nèi)回調(diào)函數(shù)的執(zhí)行。因此利用并將依賴置為使代碼在所有渲染周期內(nèi),只在初始化執(zhí)行一次。同時(shí)代碼里還對(duì)等公共方法進(jìn)行了包裝,讓這些回調(diào)函數(shù)中自帶效果。前端精讀幫你篩選靠譜的內(nèi)容。 1. 引言 react-easy-state 是個(gè)比較有趣的庫(kù),利用 Proxy 創(chuàng)建了一個(gè)非常易用的全局?jǐn)?shù)據(jù)流管理方式。 import React from react; import { stor...
摘要:而在第二個(gè)參數(shù)中我們輸出了改變后的即第五行輸出,表明我們的更改生效了。而在的回調(diào)內(nèi),我們還調(diào)用了一個(gè)定義于內(nèi)的事件函數(shù),但是該事件函數(shù)內(nèi)的也是同步的形式。 在react中,setState是用以改變class組件狀態(tài)的函數(shù),它有兩種用法:一 傳入一個(gè)updater函數(shù),該函數(shù)有兩個(gè)參數(shù),一個(gè)是當(dāng)前的state,還有一個(gè)是當(dāng)前的props。該函數(shù)的返回值需要是一個(gè)更改的state值的對(duì)象...
摘要:的作用在文檔中是這么說(shuō)的給下級(jí)組件中的提供可用的的對(duì)象。這個(gè)文件里的主要是被方法引入,并傳給的,算是一個(gè)默認(rèn)的。表示當(dāng)前的名稱。這個(gè)值表示在里面的值。便于控制,同時(shí)某些不需要渲染的,也不會(huì)造成渲染。 注:這篇文章只是講解React Redux這一層,并不包含Redux部分。Redux有計(jì)劃去學(xué)習(xí),等以后學(xué)習(xí)了Redux源碼以后再做分析注:代碼基于現(xiàn)在(2016.12.29)React ...
react組件 參考:https://facebook.github.io/re... react的組件是其核心思想部分,react允許將整個(gè)ui設(shè)計(jì)分割稱為獨(dú)立的、可復(fù)用的隔離模塊,react的組件是一個(gè)抽象的類,直接使用reacy.component是沒(méi)有很大意義的,所以一般使用的方法就是定義一個(gè) class 來(lái)繼承這個(gè)component,并且需要實(shí)現(xiàn)方法 render();就像下面一樣: ...
react組件 參考:https://facebook.github.io/re... react的組件是其核心思想部分,react允許將整個(gè)ui設(shè)計(jì)分割稱為獨(dú)立的、可復(fù)用的隔離模塊,react的組件是一個(gè)抽象的類,直接使用reacy.component是沒(méi)有很大意義的,所以一般使用的方法就是定義一個(gè) class 來(lái)繼承這個(gè)component,并且需要實(shí)現(xiàn)方法 render();就像下面一樣: ...
閱讀 2544·2021-11-18 10:02
閱讀 2027·2021-11-09 09:45
閱讀 2510·2021-09-26 09:47
閱讀 1070·2021-07-23 10:26
閱讀 1110·2019-08-30 15:47
閱讀 3390·2019-08-30 15:44
閱讀 1008·2019-08-30 15:43
閱讀 915·2019-08-29 13:50