從零實現(xiàn)富文本編輯器#11-Immutable狀態(tài)維護與增量渲染

在先前我們討論了視圖層的適配器設(shè)計,主要是全量的視圖初始化渲染,包括生命周期同步、狀態(tài)管理、渲染模式、DOM映射狀態(tài)等。在這里我們需要處理變更的增量更新,這屬于性能方面的考量,需要考慮如何實現(xiàn)不可變的狀態(tài)對象,以此來實現(xiàn)Op操作以及最小化DOM變更。

行級不可變狀態(tài)

在這里我們先不引入視圖層的渲染問題,而是僅在Model層面上實現(xiàn)精細(xì)化的處理,具體來說就是實現(xiàn)不可變的狀態(tài)對象,僅更新的節(jié)點才會被重新創(chuàng)建,其他節(jié)點則直接復(fù)用。由此想來此模塊的實現(xiàn)頗為復(fù)雜,也并未引入immer等框架,而是直接處理的狀態(tài)對象,因此先從簡單的更新模式開始考慮。

回到最開始實現(xiàn)的State模塊更新文檔內(nèi)容,我們是直接重建了所有的LineState以及LeafState對象,然后在React視圖層的BlockModel中監(jiān)聽了OnContentChange事件,以此來將BlockState的更新應(yīng)用到視圖層。

這種方式簡單直接,全量更新狀態(tài)能夠保證在React的狀態(tài)更新,然而這種方式的問題在于性能。當(dāng)文檔內(nèi)容非常大的時候,全量計算將會導(dǎo)致大量的狀態(tài)重建,并且其本身的改變也會導(dǎo)致React的diff差異進而全量更新文檔視圖,這樣的性能開銷通常是不可接受的。

那么通常來說我們就需要基于變更來確定狀態(tài)的更新,首先我們需要確定更新的粒度,例如以行為基準(zhǔn)則未變更的時候就直接取原有的LineState。相當(dāng)于盡可能復(fù)用Origin List然后生成Target List,這樣的方式自然可以避免部分狀態(tài)的重建,盡可能復(fù)用原本的對象。

整體思路大概是先執(zhí)行變成生成最新的列表,然后分別設(shè)置舊列表和新列表的row和col兩個指針值,然后更新時記錄起始row,刪除和新增自然是正常處理,對于更新則認(rèn)為是先刪后增。對于內(nèi)容的處理則需要分別討論單行和跨行的問題,中間部分的內(nèi)容就作為重建的操作。

最后可以將這部分增刪LineState數(shù)據(jù)放置于Changes中,就可以得到實際增刪的Ops了,這樣我們就可以優(yōu)化部分的性能,因為僅原列表和目標(biāo)列表的中間部分才會重建,其他部分的行狀態(tài)直接復(fù)用。此外這部分?jǐn)?shù)據(jù)在apply的delta中是不存在的,同樣可以認(rèn)為是數(shù)據(jù)的補充。

那么這里實際上是存在非常需要關(guān)注的點,我們現(xiàn)在維護的是狀態(tài)模型,也就是說所有的更新就不再是直接的compose,而是操作我們實現(xiàn)的狀態(tài)對象。本質(zhì)上我們是需要實現(xiàn)行級別的compose方法,這里的實現(xiàn)非常重要,假如我們對于數(shù)據(jù)的處理存在偏差的話,那么就會導(dǎo)致狀態(tài)出現(xiàn)問題。

此外在這種方式中,我們判斷LineState是否需要新建則是根據(jù)整個行內(nèi)的所有LeafState來重建的。也就是說這種時候我們是需要再次將所有的op遍歷一遍,當(dāng)然實際上由于最后還需要將compose后的Delta切割為行級別的內(nèi)容,所以其實即使在應(yīng)用變更后也最少需要再遍歷兩次。

那么此時我們需要思考優(yōu)化方向,首先是首個retain,在這里我們應(yīng)該直接完整復(fù)用原本的LineState,包括處理后的剩余節(jié)點也是如此。而對于中間的節(jié)點,我們就需要為其獨立設(shè)計更新策略,這部分理論上來說是需要完全獨立處理為新的狀態(tài)對象的,這樣可以減少部分Leaf Op的遍歷。

其中,如果是新建的節(jié)點,我們直接構(gòu)建新的LineState即可,刪除的節(jié)點則不從原本的LineState中放置于新的列表。而對于更新的節(jié)點,我們需要更新原本的LineState對象,因為實際上行是存在更新的,而重點是我們需要將原本的LineState的key值復(fù)用。

這里我們先簡單實現(xiàn)實現(xiàn)描述一下復(fù)用的問題,比較方便的實現(xiàn)則是直接以\n的標(biāo)識為目標(biāo)的State,這就意味著我們要獨立\n為獨立的狀態(tài)。即如果在123|456\n的|位置插入\n的話,那么我們就是123是新的LineState,456是原本的LineState,以此來實現(xiàn)key的復(fù)用。

其實這里有個非常值得關(guān)注的點是,LineState在Delta中是沒有具體對應(yīng)的Op的,而相對應(yīng)的LeafState則是有具體的Op的。這就意味著我們在處理LineState的更新時,是不能直接根據(jù)變更控制的,因此必須要找到能夠映射的狀態(tài),因此最簡單的方案即根據(jù)\n節(jié)點映射。

patek-shs.sxjshdzb.com

patek-bjs.sxjshdzb.com

patek-hzs.sxjshdzb.com

patek-shs.xajshdzb.com

patek-hzs.xajshdzb.com

patek-cds.xajshdzb.com

patek-gebs.xajshdzb.com

patek-tjs.xajshdzb.com

patek-shs.sxjshd.com

patek-gebs.sxjshd.com

patek-shs.watch51.com

patek-bjs.watch51.com

patek-hzs.watch51.com

patek-cds.watch51.com

patek-gebs.watch51.com

patek-hzs.jshdcq.com

patek-gebs.jshdcq.com

patek-tjs.jshdcq.com

patek-shs.richardweixiu.com

patek-bjs.richardweixiu.com

patek-hzs.richardweixiu.com

patek-shs.watchjt.com

patek-hzs.watchjt.com

patek-cds.watchjt.com

patek-gebs.watchjt.com

patek-jns.watchjt.com

patek-shs.jshdzg.com

patek-wxs.jshdzg.com

patek-shs.jshdsx.com

patek-bjs.jshdsx.com

patek-hzs.jshdsx.com

patek-cds.jshdsx.com

patek-wxs.jshdsx.com

patek-hzs.guoshew.com

patek-wxs.guoshew.com

patek-jns.guoshew.com

patek-shs.ncjshdzb.com

patek-bjs.ncjshdzb.com

patek-hzs.ncjshdzb.com

patek-hzs.jsddshwx.com

patek-wxs.jsddshwx.com

patek-ccs.jsddshwx.com

patek-dgs.jsddshwx.com

patek-ncs.jsddshwx.com

patek-hzs.hnjshdzb.com

patek-tys.hnjshdzb.com

patek-hzs.hljjshd.com

patek-cds.hljjshd.com

patek-wxs.hljjshd.com

patek-ccs.hljjshd.com

實際上我們可以總結(jié)一下,最開始我們考慮先更新再diff,后來考慮的是邊更新邊記錄。邊更新邊記錄的優(yōu)點在于,可以避免再次遍歷一邊所有Leaf節(jié)點的消耗,同時也可以避免diff的復(fù)雜性。但是這里也存在個問題,如果內(nèi)部進行了多次retain操作,則無法直接復(fù)用LineState。

不過通常來說,最高頻的操作是輸入內(nèi)容,這種情況下首操作一般都是retain,尾操作為空會收集剩余文檔內(nèi)容,因此這部分優(yōu)化是會被高頻觸發(fā)的。而如果是多次的內(nèi)容部分變更操作,這部分雖然可以通過判斷行內(nèi)的葉子結(jié)點是否變更,來判斷是否復(fù)用行對象,但是也存在一定復(fù)雜性。

關(guān)于這部分的具體實現(xiàn),在編輯器的狀態(tài)模塊里存在獨立的Mutate模塊,這部分實現(xiàn)在后邊實現(xiàn)各個模塊時會獨立介紹。到這里我們就可以實現(xiàn)一個簡單的Immutable狀態(tài)維護,如果Leaf節(jié)點發(fā)生變化之后,其父節(jié)點Line會觸發(fā)更新,而其他節(jié)點則可以直接復(fù)用。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容