SPA的巢狀樹資料結構怎麼管理和渲染

Lululala

Jul 20, 2021 Published

巢狀資料的應用像是Facebook的留言, Wix網頁編輯器, 文字編輯器等等,或多或少都會需要做到巢狀資料的管理,但網路上鮮少找到可以管理這些結構的好方式或概念,而這篇會述說如何怎麼實踐這些功能的核心。

此篇會拿過去曾做了一個私人專案Pagihub來講解,主要概念是結合Sketch的彈性讓組件可無限巢狀且和WIX一樣有響應式功能,設計師可以做出具有設計感的網頁,裡面無論大小物件都可以是一個組件像Github一樣被分享,希望未來工程師不用再刻板。

Pagihub後端被我拿去做別的專案,陰錯陽差整個被我搞爆所以拆掉40%的功能包括上傳圖片、組建分享和發佈,裡面60%核心還是可以正常試用,但主要TA和UX是給設計師而不是工程師使用,工程師試用時可以會不習慣。

經過大改底層結構10次才找到一個效能好、Code少、且好維護的方式來管理巢狀資料

這裡會用較複雜使用場景,但只要學到核心概念,之後就可以自己簡化實作在其他應用上!

先說說你要解決巢狀資料情境有多複雜?

以Pagihub為例,一個Object都代表著一段字、一張照片、一個Carousel或一個容器,每個Object都可以一直無限巢狀下去。

譬如Carousel裡面可以有多個照片和文字,每一頁的Carousel內部可以再放入其他巢狀組件。

為什麼要管理到這麼細節?

  1. JSON有一個地方變動時,整個畫面重新渲懶一次?儘管React/Vue有虛擬Dom不會真的重新渲染,但還是會浪費效能、時間整份跑一次做比對,React也更需要寫判斷阻止無效渲染

  2. 文字編輯器,2000個字只改了一個符號,就把整篇2000個字傳去後端存嗎?

  3. 網頁編輯器,你只改了一個顏色,就把整份JSON傳去後端存嗎?

  4. Redo, Undo每次有變更,就直接存整份狀態?

技術上會希望的是,當每改一個字或一個值能準確地記住是哪個字哪個位置改了,且只重新渲染那個值所影響的畫面!

核心概念

1. Server 和 SPA 的資料結構一定要用二維陣列表達,子組建只需要知道父組件即可

store/components.js

2. 從二維陣列算出父組件和子組建的關係

將server拿到的陣列轉為以下結構,中間的演算法可參考list-to-tree,我是把這個套件的核心拔出來自己改寫過~

store/components.js

當nodes有新的組件或刪除組件,就要同時更新childrenOf的值,請記住這裡的nodes和childrenOf,步驟4會提到這裡

3. 核心重點!渲染時父層只要知道小孩是誰就好,子層則只需要管好自己的值

Carousel.vue

CarouselItem.vue

Text.vue

4. 改值怎麼辦?新增組件?刪除組件?

請把function統一寫在Vuex或Redux等地方管理,組建直接呼叫,而不是散落世界各地

其實不建議Vuex或Redux來實踐,Vue建議用Provide/Inject,React建議用useContext

  • Carousel想要新增/刪除CarouselItem怎麼辦? 統一直接對store裡面的nodes, childrenOf 做改變,畫面則會自動渲染

store.components.js

  • Text的文字想要修改怎麼辦, Carousel想要多個背景怎麼辦??

store.components.js

如何精準在任意在Array, Object裡面的某個地方做儲存和修改來管理組件值

我自己寫了個套件在管理樹狀資料改值和Redo,Undo的部分,甚至可以處理極其誇張的樹狀結構。大部分人其實不需要這麼複雜需求,自己可以考慮簡單寫一個。

結論

  • 為什麼效能好?

每個組件的變更只影響自己,甚至每個值都直接對應需要重新渲染的部分,沒有其他多餘的消耗

  • 為什麼好維護?

組件的Props只需要一個 ID, 且所有資料直接跟store溝通,組建耦合地極低,當父組件銷毀時,子組件也會自動消失

  • 核心程式碼簡單

Pagihub核心程式碼只有200行,相信大家需求較簡單應該100行內可以解決此複雜的難題

Vue 資料管理