頁面搭建工具總結及架構思考
在初步完成了線上流程圖編輯工具之後,又接到了線上搭建頁面工具的需求,剛開始其實並不想接專案,因為從歷史以及現實原因來看,個性化及動態渲染都是很難解決的痛點,各種H5頁面搭建工具的不溫不火早已說明了這條路並沒有這麼好走,但從另一個方面來說,既然有了這樣的需求,那也就說明了現實的工作流程確實存在問題,本著解決問題的心態,說不定可以從工具及產生的新式元件部分重新梳理底層開發規範,從而側面增強我司前端部門的重要程度.人總要有夢想是吧...
簡介
專案初版部分UI 元件使用了ant design加快進度.到目前為止基本解決內部建立頁面模板,併發布到使用者伺服器的流程.
目標
通過托拉拽方式快速生成頁面模板,進行預覽及釋出.
使用人群分析
普通使用者需要所見即碎得的編輯方式,通過簡單,學習成本低的操作,比如拖拽頁面元素的方式來達到快速生成頁面的效果.當然,當頁面元素過多時,實際上拖拽的方式,就不如樹形結構容易定位和編輯(可參考ps等軟體).
而對於運營人員甚至是原模板製作人員來說,除了希望通過操作已有元件進行頁面排版之外,還希望可以通過屬性配置及方法修改來完成獨立業務場景的活動頁面.
對於中後臺系統開發人員來說,大量的表單頁面以及更加複雜的業務流程流轉,不僅需要頁面搭建工具,甚至做線上的IDE都不為過,當然現階段版本暫時不需要這種複雜場景.但在考慮整體架構時,仍然需要針對這一情況進行分析.
為什麼使用視覺化搭建工具,而不是自研UI框架?
一方面工具能夠提高內部開發效率,更快完成修改及簡單製作需求,而自研UI框架的難點在於如何持續投入,對於中小企業而言,盈利才是最終目標,就是對於模板開發人員來說,一個擁有眾多樣例及解決方案的開源庫,在實際工作中也能節省大量的時間.之前內部失敗的幾個前端UI元件庫已經說明了這一點,開發成本,實際用例,問題解決,相關文件都會帶來各種各樣的問題.
另一方面,業務的大量擴張,各個業務線所要求的人力成本越來越高,逐漸走向惡性迴圈.從使用者角度來說,越來越長的交付時間帶來的是體驗與質量的大幅下降,阻礙了進一步的發展.
結合實際情況
這次的重點其實在於如何提高初級前端開發者的開發效率以及解決運營人員的上線後可維護的問題.那麼,從產品形態來說,可以參考Dreamweaver
另外,通過提供部分開放的程式碼編輯能力,來解決特殊的個性化需求.
這麼看來,我一開始想的使用現有前端框架進行改造升級以及推動新的元件開發規範根本不現實.
因此,決定工具本身使用react進行實現,元件部分結合已有的後臺模板字串方式,劃分好樣式及指令碼名稱空間,按需載入即可.
功能實現
按照上述梳理,得出工具核心功能在於編輯狀態下的拖拽佈局與元件屬性編輯以及預覽模式下的解析展示部分.再按照實際情況定義新的元件開發規範,並準備好元件管理系統,就能完成初版目標.
這裡的元件仍然採用原始的HTML+js+css的開發方式,所不同則是元件包會經過後臺轉義以及前臺解析按照規則書寫的模板字串,並輸出到頁面中進行展示.
資料結構
在之前的專案中,到後期才發現因為自實現的狀態管理工具過於簡單且缺乏文件,導致後期接手維護人員的難以修改.正好藉著專案的機會,使用與react搭配較好的 redux進行開發.
上個專案採用的扁平化狀態樹結構在一開始只是為了查詢方便,在學習了redux之後,發現正規化化的state更加符合複雜的場景,特別是在排序以及關聯查詢方面.
專案中需要實現元件節點樹
及與節點相關聯的對應視窗標題的資料
設計檢視
雖然已很久不用Dreamweaver,但確實不得不承認其所見即所得製作頁面效果在以前還是很讓人驚豔的.
而本專案中就功能程度而言肯定是遠遠不及的,以H5製作工具,或者各種原型工具來說(最近在用 xiaopiu.com,表單項設計很出彩),都是採用類ps的畫布方式.
而在本專案中,因為需要考慮運營人員的使用場景,採用全畫布形式最麻煩的地方在於以製作設計圖的思維在製作網頁,這樣會帶來製作步驟複雜,且層級較多,頁面節點一多各種互相遮蓋難以維護.這只是操作上帶來的問題,更麻煩的地方在於難以形成規範,這裡的規範是製作頁面的規範,如果只注重定位而不注重文件流自然佈局,則會在區塊劃分上定義模糊不清,對於現今大多數動態獲取資料模板來說,也根本沒辦法定死位置.
那麼,只要搞定設計檢視中最麻煩的節點拖拽排序功能,就能完成最基礎的設計檢視功能.
拖拽功能
這裡使用了react-dnd,API定義及資料處理與DOM分離的方式確實讓我大開眼界,內部已使用redux的架構進行設計,核心部分都是純函式,將DOM操作分離出去,也因此更容易進行擴充套件,後續會專門整理原始碼閱讀筆記,以供參考.
需要支援平級排序與巢狀排序,包括浮動定位.
在使用Dnd的過程中,需要給檢視中可排序以及可巢狀放置的節點都設定型別,類似於唯一識別符號,當繫結的DOM節點觸發事件之後,會通過之前已包裹完成的高階元件進行傳遞狀態,從而觸發自定義的事件.
經過幾次的修改,最終確定了定義可巢狀的佈局容器以及無法再次巢狀只能排序的展示模組
比較麻煩的處理在於限制拖拽即時排序方法,當有巢狀層級時,過於靈活的直接插入在用於深層級巢狀時反而讓使用者無所適從,比如想要做一個第二層級排序操作,但由於使用了即時排序,如果還有內嵌層級,當移動進入時會進行插入操作.當然這一點本身也是因為思慮不周,認為只要滑鼠經過容器之上,既然無法判斷使用者是否需要插入巢狀層級,那麼直接做插入處理,但真實對接業務場景卻發現有些容器內部因為自身內容過長,導致容易誤操作,之後約束了hover事件,採用drop事件用於判斷真實巢狀意圖或排序.
對於巢狀容器而言,排序時需要根據當前拖動節點與滑鼠經過節點的實際經過位置以及之前的排序方式作判斷,比如當經過可排序節點50%高度則自動做排序,對於可巢狀放置節點,需要記錄移出時方位,當經過該節點之後,再做相應排序規則.
同時,需要定義一個畫布級容器用於滿足浮動定位需求,當展示型模組進行拖拽操作時,可放置在該容器中用於解決浮動定位排版需求.
部分互動操作
還有一個麻煩之處在於設計檢視中需要能夠支援外部樣式表以及外部指令碼,那麼為了不與工具本身產生衝突,甚至影響到工具操作,這裡的設計檢視中採用iframe作為展示層.參考react-frame-component,新建一個空白的iframe,在建立完成時手動重寫document,前期直接使用ReactDOM.unstable_renderSubtreeIntoContainer
進行跨節點元件更新,在之後因為多次重複渲染的問題改成為ReactDOM.createPortal
.
設計檢視採用iframe來實現的問題,還在於需要根據上下文進行重構元件,其中畫布類容器中的拖拽放大縮小及框選對齊及同時編輯都需要根據iframe 的document重新計算.這裡採用之前專案的邏輯,用react的方式進行元件重構即可.
屬性編輯
依託於強大的狀態管理工具,我們將表單項與節點資料進行雙向繫結即可達到屬性編輯的效果,那麼這裡只剩下對於展示型模組的屬性宣告.
不是IDE
其實工具本身起到的作用只是將程式碼以更加通用的方式進行組合,在大部分實施及運營人員沒有辦法很快速的掌握元件化開發的情況下,也需要通過工程化的手段進行推動.至少通過工具及推出的新式元件開發規範,能夠將之間那種低效的人工方式進行優化升級,包括也能夠有一定的積累.更別說分離的模式更有利於自動化測試.
重新定義XML檔案,用於支援展示型模組的配置需求,這也算是一種宣告,效果如下:
同一型別模組可以有多個展示(HTMl結構及獨立功能),通用性配置項寫在模組主XML中,展示包具體內容結構如下:
css部分會在上傳至應用中心之後進行編譯,類css module方案增加識別符號,區域性例項採用模板引擎組合的方式,生成不重複的root className 進行包裝.
主體結構部分採用html模板字串方式,定義key-value形式的資料來源,在設計檢視中根據使用者操作表單項進行資料修改,達到實時編輯效果.在內部使用時還需開放區域性的原始碼編輯效果以滿足快速修改上線.
而工具內部對應進行節點資料儲存,最終拼裝完成一個模板頁面所需的元件節點樹.這裡實際上將工具自身實現進行了隱藏,普通模板開發人員或許並不瞭解其內部元件的實現,但可以按照之前的經驗進行模板片段的編寫,唯一區別則是需要增加模板字串的認知及編寫.作為過渡方案來說,至少解決了現實成本問題.
表單項擴充套件
表單項描述中增加級聯識別符號asFor與asForValue,由工具進行判斷是否顯示當前表單項,同時增加分類識別符號classify,用於將過多的表單項進行組合分類展示.對與展示模組開發者來說,只需要宣告type即可呼叫.後續通過擴充套件提供更多型別
解析翻譯
在通過拖拽及配置的方式完成設計檢視之後,只要再解析翻譯為預覽展示所需的各終端真實內容即可.這裡只需翻譯為HTML字串即可.
終點or起點
初始的需求到這裡基本已完結,除了業務需求之外,我其實仍想探索出如何結合現實情況推動團隊的前端工程化發展,如果按照前端工程——基礎篇的階段總結,那麼我們甚至還在第一階段掙扎前行.
而在基本完成上述工具之後,我不由的思考,如果以工具作為起點,潛移默化的將高階能力植入到線上專案中去,應當比硬推框架效果更好,同時也能夠進行積累.比如這次的頁面模板,在以前甚至連最基礎的資源壓縮都沒有做到,而通過新的工具統一進行壓縮,既沒有增加開發人員的工作量,又達到了效果.通過快捷工具的方式既減輕了使用者的開發壓力,又增強了線上專案的實際質量,何樂而不為?更別說工具本身以及所產生的衍生物的價值.
架構思考
做組合
每個能夠延伸下去的業務場景其實都很複雜,如果真按照之前的模式給每一個場景單獨開發工具,那麼專案週期就太長了.特別是一旦工具內部做互動性功能,比如建站工具中嵌入問卷,或者資料大屏中加入流程流轉,不統籌兼顧根本玩不轉.
為什麼需要做組合?一部分原因當然是為了節省開發人力,另外一部分則是這些場景都有一定的共性:建站中的展示型模組,自定義表單中的表單項,甚至是資料圖表以及流程節點,都能變成資料節點進行組裝.
每一種工具最終的產物其實都只是程式碼的組合,所不同的是之前靠人編輯,現在用工具生成.只考慮通用的業務場景,再將其進行歸納,實際上已經解決了大部分的線上開發效率問題.
通過分析可以看到:這些場景都需要一個能夠進行托拉拽操作的畫布,一個畫布構成的最小單元(以下稱為節點)來進行排版,一套直觀的邏輯輔助編輯工具(這裡採用流程圖)控制事件,一個可真實渲染的設計檢視(編輯狀態,生成執行時所需的store),以及對應終端平臺的翻譯器(預覽狀態,解析之前生成的store並按規則進行渲染)就能做到組合.
nodeTree
控制整個應用渲染的頁面結構的資料.定義規範基礎結構:
對節點樹操作的行為由框架直接提供,對拖拽行為進行封裝,並提供回撥即可.
對於各個場景下所需的展示型模組來說,只需要遵循統一規範,由框架進行抽象即可
節點註冊
目前看來仍然採用之前的XMl檔案定義即可,對於額外的資料資訊,比如問卷系統中需要的事件或流程圖需要的連線等都通過關聯表的形式,進行資料擴充套件.
事件
一部分由全域性定義公共事件,比如頁面初始化以及節點銷燬,關聯狀態重置.另一部分由模組單獨宣告,比如問卷中表單項條件跳轉,在模組中宣告onChange事件,由使用者通過流程圖工具繫結對應節點.這裡的觸發效果應當由公共事件與模組修改自身屬性事件共同擴充套件.當然,這裡不需要做到自定義的程度,只要根據預先設定的規則進行處理即可.
能力擴充套件
這裡的能力擴充套件是為了分離核心功能,當工具需要比如元件樹展示,格式刷等額外功能時,通過類似中介軟體形式增加工具能力,外掛可使用內部資料流進行功能擴充套件
同時在框架中提供預留UI渲染節點:
<div id="toolbar" /> <!-- 工具欄節點注入 -->
<div id="artboard" /> <!-- 畫布頁面注入,注意不能遮擋元件節點,按照規範定義層級 -->
<div id="sidebar" /> <!-- 側邊欄節點注入 -->
節點能力定義
所有節點皆採用滑鼠移動至上方,顯示可拖動區域及快捷選單的方式
總結
在這一過程中,既有產物,又能重新梳理元件開發規範,做到由上到下統一整合,避免之前的那種割裂式開發流程,這樣即使前端框架或引用的庫發生了變換,只要根據規範進行宣告即可,在做到這一步的基礎上,再談其它,我想應該就會容易很多.
工具本身也是為了解放前端工程師,畢竟日復一日的業務程式碼的堆積很容易消耗工作的積極性,沒有人天生想一直成為資源池的一部分.通過建立新的基礎研發體系,讓工程師站在不同的角度看待問題,即使是無聊的業務程式碼,經過包裝,重新宣告,附加配置項等步驟之後,也會發現哪裡可以複用,哪裡寫的有問題等之前忽略的資訊.我覺得這才最有價值的地方