IoT Studio可视化搭建平台编辑历史功能的思考与探索
簡介:?在前端可視化搭建領(lǐng)域中“重做”和“撤銷”這兩個功能已經(jīng)是標(biāo)配中的標(biāo)配,畢竟只要有用戶行為的地方就可能會有出錯,這兩個功能無疑就是為用戶提供了“后悔藥”。目前有各種各樣的可視化搭建平臺,本文介紹IoT Studio可視化搭建平臺在編輯歷史功能上的設(shè)計與思考。
作者 | 遠(yuǎn)坂
來源 | 阿里技術(shù)公眾號
一 背景
在前端可視化搭建領(lǐng)域中“重做”和“撤銷”這兩個功能已經(jīng)是標(biāo)配中的標(biāo)配,畢竟只要有用戶行為的地方就可能會有出錯,這兩個功能無疑就是為用戶提供了“后悔藥”。目前有各種各樣的可視化搭建平臺,本文介紹IoT Studio可視化搭建平臺在編輯歷史功能上的設(shè)計與思考。
二 實現(xiàn)思路
1 頁面DSL的維護(hù)
在IoT Studio可視化搭建平臺中,我們通過頁面的抽象語法樹來維護(hù)頁面狀態(tài),頁面信息和組件信息都記錄在對應(yīng)節(jié)點上:
PageNode: {componentName: 'page1',id: 'page1',props: {},children: [ComponentNode: {componentName: 'component1',id: 'component1',props: {width: 800,height: 1000,color: '#ffffff'},children: []},ComponentNode: {componentName: 'component2',id: 'component2',props: {},children: []},ComponentNode: {componentName: 'component3',id: 'component3',props: {},children: []},] }在頁面保存時,頁面配置會作為JSON文件上傳至OSS。
2 重做與撤銷
快照法
在每次編輯頁面時,將頁面的信息進(jìn)行深拷貝存入歷史記錄中。在進(jìn)行重做和撤銷時從歷史記錄中取出對應(yīng)的快照,用快照代替當(dāng)前頁面狀態(tài),即可完成一次歷史記錄的操作。
在這種方法下,通常使用一個指針來指向當(dāng)前的頁面狀態(tài)。如下圖:
進(jìn)行后退操作后,指針指向之前的某次快照,頁面恢復(fù)到P3時的狀態(tài):
再次進(jìn)行編輯時,指針指向新的狀態(tài)P5 :
快照法的特點:
指令法
IoT Studio使用的是這種方法。
我們?yōu)槊恳淮尾僮鞫x兩個方法:execute與undo,以及將“操作”抽象為Operation。
在execute中執(zhí)行這次操作的正向操作,在undo中實現(xiàn)逆向操作。
export abstract class Operation<T = void> { /*** 逆向操作*/protected abstract undo(): T;/*** 正向操作*/protected abstract execute(): T; }每進(jìn)行一次編輯操作,其實就是創(chuàng)建一次Operation并執(zhí)行其execute方法,隨后如果需要撤銷就執(zhí)行其undo方法。
指令法的特點:
3 實現(xiàn)細(xì)節(jié)
在上文里提到了IoT Studio使用的是指令法。
Transation
在實際業(yè)務(wù)開發(fā)中,很多場景會涉及到一次性編輯多個組件,即涉及多個Operation實例。于是在Operation基礎(chǔ)上有了Transaction——事務(wù)的概念,Transaction下維護(hù)了一份Operation實例List,每當(dāng)有execute或者undo執(zhí)行時,會遍歷Operation List中的Operation實例執(zhí)行其execute或undo方法。
雙向鏈表
IoT Studio中的操作歷史是基于雙向鏈表實現(xiàn)的,每個鏈表節(jié)點維護(hù)一個Transaction實例。鏈表節(jié)點末端的execute結(jié)果既是最新的操作歷史。
鏈表之前通過forwardCurrent和backforwardCurrent方法進(jìn)行節(jié)點狀態(tài)的切換。
Class Manager {backwardCurrent(): boolean {if (this._current?.prev) {this._current.value.operation.undo();this._current = this._current.prev;this._validLength -= 1;return true;}return false;}forwardCurrent(): boolean {if (this._current?.next) {this._current.next.value.operation.execute();this._current = this._current.next;this._validLength += 1;return true;}return false;}addAfterCurrent(item: OperationResult<any>) {if (nextNode) {nextNode.prev = undefined;this._length = this._validLength;}this._current.next = { value, prev: this._current };this._current = this._current.next;} }每當(dāng)有新的編輯操作時,會通過addAfterCurrent插入新的節(jié)點。
4 總結(jié)
Operation是實現(xiàn)重做和撤銷的最小指令實例,通過Operation不同子類實現(xiàn)不同的execute和undo方法,從而實現(xiàn)重做和撤銷的具體邏輯。
Transaction中維護(hù)了Operation實例數(shù)組,我們在進(jìn)行業(yè)務(wù)邏輯開發(fā)中對組件進(jìn)行屬性設(shè)置時是以Transaction實例為單位進(jìn)行業(yè)務(wù)邏輯開發(fā)。
維護(hù)了一個雙向鏈表來對Transaction實例進(jìn)行管理,從而實現(xiàn)可視化搭建的操作歷史功能。
三 探索
在實現(xiàn)思路中我們提到了“快照法”和“指令法”,對比兩者的優(yōu)缺點,不難發(fā)現(xiàn)主要矛盾是在體積與維護(hù)成本上。那么有沒有一種辦法能兼顧二者的優(yōu)點呢?下面兩個工具可以提供一些思路:
immutable.js + 快照法
在JS中對象是引用賦值,在保存對象時往往會使用深拷貝規(guī)避這個問題,但是這樣會造成CPU和內(nèi)存的浪費,這也是快照法的缺點所在。
immutable使用持久化數(shù)據(jù)結(jié)構(gòu),在使用舊數(shù)據(jù)創(chuàng)建新數(shù)據(jù)的時候,會保證舊數(shù)據(jù)同時可用且不變,同時為了避免深度復(fù)制復(fù)制所有節(jié)點的帶來的性能損耗,immutable使用了結(jié)構(gòu)共享,即如果對象樹種的一個節(jié)點發(fā)生變化,只修改這個節(jié)點和受他影響的父節(jié)點,其他節(jié)點則共享。在實現(xiàn)操作歷史功能時,使用immutable存儲數(shù)據(jù),能解決數(shù)據(jù)復(fù)用的問題。immutable.js + 快照法可以組合使用。據(jù)我所知公司的@ali/visualengine使用的就是這個方案。
Git
每次我們運行 git add 和 git commit 命令時,Git 所做的工作實質(zhì)就是將被改寫的文件保存為數(shù)據(jù)對象, 更新暫存區(qū),記錄樹對象。我們在使用git維護(hù)項目時,理論上隨著git commit的次數(shù)越來越多,文件對象會越拉越大,但實際上體積并沒有變的很大。事實上git在權(quán)衡時間和空間后幫我們做了部分優(yōu)化,較早的版本會保存diff,較新的本會保存全量數(shù)據(jù)對象。
Git 是如何做到這點的?Git 打包對象時,會查找命名及大小相近的文件,并只保存文件不同版本之間的差異內(nèi)容。 你可以查看包文件,觀察它是如何節(jié)省空間的。同樣有趣的地方在于,第二個版本完整保存了文件內(nèi)容,而原始的版本反而是以差異方式保存的——這是因為大部分情況下需要快速訪問文件的最新版本。最妙之處是你可以隨時重新打包。Git 時常會自動對倉庫進(jìn)行重新打包以節(jié)省空間。當(dāng)然你也可以隨時手動執(zhí)行 git gc 命令來這么做。
原文鏈接
本文為阿里云原創(chuàng)內(nèi)容,未經(jīng)允許不得轉(zhuǎn)載。?
總結(jié)
以上是生活随笔為你收集整理的IoT Studio可视化搭建平台编辑历史功能的思考与探索的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 基于MaxCompute分布式Pytho
- 下一篇: 如何进行基于Anolis OS的企业级J