在初步完成了 "線上流程圖編輯工具" 之後,又接到了線上搭建頁面工具的需求,剛開始其實並不想接項目,因為從歷史以及現實原因來看,個性化及動態渲染都是很難解決的痛點,各種H5頁面搭建工具的不溫不火早已說明瞭這條路並沒有這麼好走,但從另一個方面來說,既然有了這樣的需求,那也就說明瞭現實的工作流程確實存在 ...
在初步完成了線上流程圖編輯工具之後,又接到了線上搭建頁面工具的需求,剛開始其實並不想接項目,因為從歷史以及現實原因來看,個性化及動態渲染都是很難解決的痛點,各種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" /> <!-- 側邊欄節點註入 -->
節點能力定義
所有節點皆採用滑鼠移動至上方,顯示可拖動區域及快捷菜單的方式
總結
在這一過程中,既有產物,又能重新梳理組件開發規範,做到由上到下統一整合,避免之前的那種割裂式開發流程,這樣即使前端框架或引用的庫發生了變換,只要根據規範進行聲明即可,在做到這一步的基礎上,再談其它,我想應該就會容易很多.
工具本身也是為瞭解放前端工程師,畢竟日復一日的業務代碼的堆積很容易消耗工作的積極性,沒有人天生想一直成為資源池的一部分.通過建立新的基礎研發體系,讓工程師站在不同的角度看待問題,即使是無聊的業務代碼,經過包裝,重新聲明,附加配置項等步驟之後,也會發現哪裡可以復用,哪裡寫的有問題等之前忽略的信息.我覺得這才最有價值的地方