所見即所得低代碼編輯器畫布實現(xiàn)(所見即所得編程語言)
1
前言
在開發(fā)或使用低代碼產品時,我們總會想到一個功能,就是預覽,用來確認編輯的內容是否符合預期。
市面上的編輯器在預覽方面有多種實現(xiàn)方式:
- 打開一個新的窗口,進行預覽
- 直接在編輯區(qū)所見即所得
如果是用戶,當然是選擇第2種,最直觀,最符合預期。
如果是開發(fā)者就會知道,之所以有這樣多的可能性,是因為所見即所得非常依賴實際生產的運行環(huán)境,第1種單獨打開窗口的實現(xiàn)成本比較低。
而有的編輯器雖然聲稱是所見即所得,但實際操作上,只是有相似度的所見即所得,因為實際運行環(huán)境和生產有較大的差距,仍然比較容易產生預期外的問題。
在我們團隊的珊瑚海項目中,就實現(xiàn)了一個可以做到完全所見即所得的低代碼編輯器,如果想了解該項目概況,可以訪問下我們過往的兩篇基本介紹文章。
- 《設計無侵入性的低代碼編輯器》
- 《珊瑚??缍私鉀Q方案及在移動端的布局動態(tài)化實踐》
下面我就來介紹下,我們是如何實現(xiàn)的所見即所得低代碼編輯畫布。
2
方案設計
在描述具體實現(xiàn)之前,先聲明一些前提:
- 本文描述的編輯器的運行容器都是瀏覽器,因此所見即所得的主要是瀏覽器的頁面
- 本文所描述的所見即所得的“得”,主要指和生產環(huán)境最終產物一致的效果,而不是高保真Demo
2.1 通常的畫布實現(xiàn)
低代碼編輯器中,通常是將組件的實際內容拖拽到編輯器的畫布中,然后展示出一個頁面的布局情況。
如果想對畫布中的元素進行編輯和修改,首先要點擊一下畫布中的元素,會出現(xiàn)對應元素的屬性配置,修改這些配置項,并將修改后的效果展示在畫布中,就完成了一個基本的低代碼編輯的操作。
這套基本的操作,在時下主流的編輯器中都是將拖拽的元素、選擇元素的事件、選框都寫在了一個文檔流中,而這樣的設計就讓畫布的內容包含了編輯器的樣式和腳本,而生產環(huán)境并沒有這些內容,這就導致在這樣的設計下,編輯器中的內容,必然無法完全做到所見即所得。
2.2 通常的預覽實現(xiàn)
基于對編輯器畫布無法完全做到所見即所得的設計,也就衍生了通過彈出一個單獨的容器(可能是一個沙盒窗口、也可能是新建一個頁面)的方式來完成生產環(huán)境頁面的預覽,這樣是可以達到預覽生產環(huán)境頁面的目的。
但是這就讓編輯和對產物的預期產生了一定的割裂,一邊編輯,還要一邊去打開一個額外的容器去看效果,顯得比較笨拙。
2.3 讓預覽作為編輯環(huán)節(jié)的一部分
由上面的鋪墊我們不難想到,如果我的畫布上編輯的內容就是預覽的內容,不就能做到直接編輯生產環(huán)境的內容了么。
那這個預覽的內容放在哪里呢?通過畫中畫的方式根據(jù)修改實時刷新,這或許不錯,但是在優(yōu)先的窗口下,預覽要看的清楚就會有些占地方。
既然畫布描繪的就是期望和生產一樣,那不如把這個預覽的窗口墊在畫布下面,甚至作為畫布的一部分是不是就能達到目的了呢?
像上圖一樣,我們將預覽的容器放在畫布區(qū)域下面,每當編輯器中的頁面產生變化,我們就重新渲染一下容器,這樣就可以達到我們在頁面中所見即所得的目的了,酷!
但是實際操作的話我們就會發(fā)現(xiàn),原有畫布和預覽容器都有內容的展示,這會導致上下層級視覺上的沖突,而且因為兩個容器的運行環(huán)境不同,也導致了內容無法一一對齊,很容易就產生了錯位,不過既然整體思路有了,下面我們就來解決問題。
3
編輯層和渲染層
我們來整理一下將預覽容器放在畫布下顯而易見的問題:
- 組件的展示會產生重疊
- 兩層內容會產生錯位
針對這兩個問題,我設計了一套多層結構畫布,即由編輯層和渲染層組成的畫布。
其中渲染層就是預覽的頁面,在這一層我們不會做什么工作,因為一旦做了什么就會影響生產版本的還原度。
也因此,渲染層的容器我選擇了iframe,用iframe可以較完美的隔絕編輯器和渲染層的上下文,不會受到意外的干擾;同時也可以在操作彈窗這種組件的時候,讓覆蓋整個頁面的蒙版更可控。
而編輯層之于原來的畫布,我們也做了一些改變,最大的區(qū)別就是編輯層不再感知當前拖拽的組件實體,而是通過一個通用的占位組件結合當前要操作的實際組件信息,組織出一個觸發(fā)器,以透明的方式遮蓋在渲染層對應的組件上方。
上圖是一個實際用戶從操作編輯層,到看到編輯結果的流程。通過這樣的設計,用戶操作編輯器的時候,看到的就是原來的預覽容器,也就是渲染層展示的內容。
而用戶去點擊渲染層組件的時候,其實點擊到的是覆蓋在渲染層上面編輯層中的觸發(fā)器,然后根據(jù)觸發(fā)器綁定的組件信息,進行實際的編輯。
在編輯過后,通過將編輯器的產物給到渲染層做重新的渲染,就有可能達到百分百的所見即所得的效果了,且解決了組件間重疊的問題。
用戶在編輯時想要點擊一個畫布中的組件,如何能讓系統(tǒng)正確地識別到用戶點擊的這個組件呢?這就要先解決兩個層級中組件錯位的問題了。
4
編輯層和渲染層組建的布局同步
為了能讓編輯層的觸發(fā)器和渲染層的實際組件布局同步,我首先嘗試的方案是通過收集渲染層組件渲染后的實際展示大小,讓編輯層的觸發(fā)器繼承對應組件的這個大小然后根據(jù)所設置的默認信息,自然形成編輯層的布局狀態(tài),來達到一一對齊。
這里“自然形成”的意思是指如果一個組件在渲染層用的是flex那編輯層對應的觸發(fā)框也用flex,而收集組件大小是因為,編輯層沒有實際的內容,如文本組件里面會因為文字內容的不同,大小不同。
而這種“自然形成”的策略在實踐中很快就出現(xiàn)了問題:
- 受到全局屬性的影響,如box-sizing在編輯層和渲染層的定義不一樣會導致渲染布局的整體邏輯都不同
- 一些組件自定義的樣式編輯層感知不到,對于這種黑盒的場景無法完全覆蓋到
尤其是組件樣式的問題,導致了這個方案直接被否定,因此我們轉向了另一個思路,獲取組件最外層的所有布局信息傳遞給編輯層。
我在設計這套DSL給定每個節(jié)點一個id,在這里我們可以通過將節(jié)點id附著在渲染組件的DOM上。
再通過DOM的getBoundingClientRect方法獲取DOMRect對象,再結合組件上的overflow屬性,我們就可以較精確的定位到組件在渲染層的實際位置和大小。
因為渲染層用的是iframe,我們在編輯器是可以獲取到渲染層的上下文的,因此通過前面的方案我可以輕松獲取到所有組件的布局信息,并和編輯層一一對應。
5
實際應用
咱們來看下實際的效果
上圖是編輯器中的實際效果,我們可以看到頁面就是生產一樣的頁面,點擊內容會觸發(fā)選框。
為什么有多個選框?這是使用了數(shù)據(jù)綁定和根據(jù)數(shù)據(jù)循環(huán)渲染節(jié)點的效果,后面的文章我們會對實現(xiàn)進行展開,本文不做展開。
我們換個圖來看下這個頁面。
在chrome的devTools中l(wèi)ayers模式下我們可以更直觀地看出整個分層的關系,每一個觸發(fā)器都清晰可見。
看到這個圖大家應該可以更直觀地理解我們的這個方案操作方式。
6
好處不止所見即所得
通過編輯層和渲染層配合得到的所見即所得的設計,我們可以再發(fā)散一下思維,看看在這樣的架構下還能做些什么。
低代碼編輯器跨技術棧的實現(xiàn):渲染層的隔離,也就是組件運行環(huán)境的隔離。稍加操作,我們完全可以讓渲染層以外的內容不用感知組件的實現(xiàn),如通過聲明的方式導入組件。
通過這樣的設計就能做到編輯器和渲染層中的組件用不同的技術棧,就能做到react開發(fā)的編輯器,拖拽vue開發(fā)的組件庫,這能讓編輯器可復用性更好,也能更加地減少開發(fā)資源。
線上頁面調試工具:我們也可以換個思維,做一些工具,如chrome的擴展工具,在系統(tǒng)產生的頁面上像收集布局信息那樣,直接植入編輯器或者做一些調試工作。
在這樣靈活的低代碼編輯器設計下,你也可以發(fā)揮想象力,做更多更酷的事情。
7
并不完美
這個設計可以讓我們的低代碼編輯器復用,且更具擴展性,但也有一定的不足:
- 知識成本:雖然能做到完美還原生產環(huán)境的狀態(tài),但是是否能真的還原依賴組件庫開發(fā)者的建設,比如生產環(huán)境所有依賴的腳本和樣式,即使和組件庫并無直接關系。
- 動態(tài)頁面的解決方案:如果是純靜態(tài)的頁面,這個設計很好理解,但如果是要調用接口呢,整個頁面是動態(tài)的呢,要怎么做到所見即所得,這里就需要編輯器綁定數(shù)據(jù)的建設,而這里我也做了設計和實踐,將在后面的文章中說明。
- 對圖文混排支持不夠:還有就是圖文混排的場景,選框不夠精準,因為行內片段可能是個多邊形,這個因為場景比較少,我還沒有想到好的方案,希望有想法的同學可以留言給我。
- 渲染次數(shù)過多導致性能問題:再有就是這樣的方案會導致編輯層和渲染層的內容重新渲染次數(shù)過多,比較消耗瀏覽器的性能。這里在后面會考慮根據(jù)場景策略加一些變更的監(jiān)聽,來減少一些渲染的回流,不過這就是后話了。
8
總結
隨著前端技術的覆蓋面越來越廣,低代碼作為前端頁面搭建的一個提效選擇,幾乎成為了前端團隊必須的技術沉淀,為了能讓低代碼系統(tǒng)本身的開發(fā)成本越來越低,大家也在做各種各樣的嘗試,編輯層和渲染層的分離也是在這個大背景下的產物。
當前阿里的低代碼引擎和騰訊的魔方平臺也都使用了類似的方案。在開源的內容中,為了讓畫布層更可控,兩個大廠都不約而同的選擇了定制化的畫布,而我在這里給出的方案是增加了一些開發(fā)的知識點,但是更加開放,把掌控權給到了開發(fā)者。
另外就像前面說的,我還考慮了數(shù)據(jù)綁定還有跨技術棧相關的場景,后續(xù)的文章中將會更新相關的內容。
作者:高飛宇
來源:微信公眾號:58技術
出處:https://mp.weixin.qq.com/s/bfZraIWD7REeCvV27iCKEw