摘要:發送請求,處理數據。在上面這個場景中,這類數據的結構可能是最常碰到的。整個過程可以簡化成數據的變化引起視圖的變化,和現在很多前端框架數據驅動思想有幾分相似。至此一個對于頁面的抽象出來的數據結構雛形基本完成了。
前言作者:周周(滬江資深Web前端開發工程師)
本文為原創文章,轉載請注明作者及出處
近期在小D十周年活動之際,又看到了一個自家H5專題夢工廠生成的頁面。
我與小D十年回憶 >>
回想起了一段往事,現在來看還蠻有趣的。主要是一個將業務逐步抽象成數據的過程,對于當時對數據設計等還不太敏感的自己有不小的促進作用。于是想通過本文分享下當初如何搭建可視化編輯頁面系統中的一些開發設計思路,也希望對前端伙伴們在構建類似中大型應用時有一定幫助,可以更好地設計一些較復雜的數據結構。本文不會把具體的實現代碼貼出來,更多的是背后為何要這樣設計的一些思考過程,如何把一些業務映射到數據。有不足之處,還請輕拍。
讓我們一起先來預覽下編輯器的后臺界面,編輯伙伴可以像在桌面應用中一樣進行操作,最后直接生成一個h5頁面。
背景當時14年正值H5比較火熱的時期,業務中很多時候會碰到一些重復類似的h5活動頁面,開發幾乎要變成 ctrl+c 和 ctrl+v 了,略顯枯燥。后來涌現出了很多h5頁面編輯應用,某秀、某KA等。某一天可愛的老大又在背后看著我們,突然來了一句:
我們要不也搞一個?
當時反應是萬匹草泥馬在頭上奔過: 這個有點太復雜了,成本太高,還是不做了吧。不過冷靜下來后,心想做完還可以解放一批開發同學,挑戰也不小,所以心想
為什么不呢?
于是就開始了一個可視化編輯頁面系統 Web IDE 的打造之旅。從計劃開始做的那一刻起,其實是腦中一片空白,后來做了一些小DEMO后,才開始有了點思緒,也不是一蹴而就的。
先舉個小例子**場景: 前后端協作,需求是在頁面上加一個異步請求的列表模塊。
1.發送請求,處理數據。接口響應回來的數據可能是:
{ ..., data: { list: [ {id: 1, content: "這是第一條數據"}, {id: 2, content: "這是第二條數據"}, {id: 3, content: "這是第三條數據"} ], total: 4 } }
2.根據 list 中的數據,循環拼接出需要渲染的DOM結構
3.找到需要追加或者替換的節點,然后加到頁面上。
在上面這個場景中,這類數據的結構可能是最常碰到的。整個過程可以理解為,先獲取數據,再把數據轉換到渲染視圖里。看目前流行的框架,react,vue 等都幫我們省略了關注 DOM 的步驟,在這里我們先拋開一些框架的便利,回歸到原始的步驟,由一個 renderer 方法充當。
+--------+ +----------+ +-------+ | data | => | renderer | => | DOM | +--------+ +----------+ +-------+
伙伴要問了,這個流程比較熟悉,但如果做一個 Web IDE 不是就這么幾步吧?
如何下筆需求分析要詳細
要下筆時,發現課題變得復雜了。很多時候就會像下面這張圖中。
那先緩一緩,先嘗試著拆解成一些小部件來分析。多觀察,多聯想,多分析。
制定目標如何把一個H5頁面轉換成一個可編輯的狀態。
現狀分析觀察一個普通的H5活動頁面,試著先抽取幾個關鍵元素。試著滑動頁面,大致可以猜測這是一個滑動組件,占滿全屏的,交互可以劃分為上下滑動,可能還有回調等等,這時可以發現一個H5上可能有一個或多個子頁面(分頁),這里面可能將會涉及到分頁的操作。再看每個分頁上,有圖片,鏈接,文字等等一些元素,展現形式差異很大,樣式都不一樣,看起來很難統一,不像上面的小例子中可以比較容易地拼湊出,先放一放。但是有兩個關鍵點浮現出來了,分頁+元素,一個h5頁面基本都是由很多頁面和元素組成的,目標將轉換成如何編輯分頁,如何編輯元素。
詳細分析再多帶帶看一個頁面詳細地分析下:
可以發現頁面中有很多元素,大致有:
圖片
音頻
視頻
文字內容
鏈接
動畫
其他元素們
試著抽象一層,一個頁面可能會變成下面的結構:
頁面: [ 元素1, 元素2, 元素3 ]
發現和“小例子”中的結構有點類似,一個塊包含多個子塊,照著類似的渲染方式,估計也能將元素們渲染到頁面上,但樣式卻各不一樣,元素之間的差異比較大,類型又不同,怎么想拼接 list 列表一樣拼呢?試著比較下,上面“小例子”中的列表數據包含的是偏內容的數據,把
類型
內容
位置
大小
顏色
背景圖
鏈接
其他特征
元素: { 類型, 內容, 位置, 大小, ... }
元素上的屬性比較龐大,但還是可以放在一個元素的對象里。設想如果把這些部分也放在該元素的數據結構上,不單單有內容數據,還有樣式上的數據,屬性上的數據等等,這樣是否就可以渲染了。那么目標有新增,如何去編輯這“龐大”的屬性集合。
試錯我們假設一段需要的帶屬性樣式的元素DOM結構:
content"s context
相比“小例子”中的
element: { style: { someKeyA: "someValueA", someKeyB: "someValueB", }, class: ["someClass"], attribute: { custom: "someCustomData", }, content: { text: "content"s context" } }
這樣的話,我們就可以通過一個拼接方法來生成我們想要的結構。這樣一個關于元素的數據結構設計就有了雛形。我們可以通過修改元素上一些屬性的值,改變元素的外在表現。整個過程可以簡化成數據的變化引起視圖的變化,和現在很多前端框架數據驅動思想有幾分相似。
整理通過類似上面很多小 demo 的積累,最后可以整理拼裝下,回到單個頁面上,除了元素,可能還有一些其他設置,假想預留一些字段。
那么一個頁面抽象下,格式大致如下:
page: { elements: [ { style: ..., class: ..., attribute: ..., content: ..., }, { element2 }, { element3 }, { element4 }, { element5 }, ], setting: { propertyA: {}, propertyB: "valueB", flagC: false, } }
生成的 DOM 結構大致如下:
...
再把一個個單頁拼起來,就變成了我們需要的H5頁面,格式大致如下:
h5: { pages: [ { elements: ..., setting: ..., }, {page2}, {page3}, {page4}, ], setting: { propertyA: {}, propertyB: "valueB", flagC: false, } }
從一個個小元素組成一個頁面,再由一個個頁面組成h5活動頁面。至此一個對于h5頁面的抽象出來的數據結構雛形基本完成了。
上面的結構沒有展開,展開后你會發現這個大對象可能上千上萬行,接下來關注下數據和操作界面中的映射關系了,如何去操作這些數據,數據怎么展現,元素和頁面的關系等等。
業務映射到數據為何要操作數據,而不是去操作DOM?
這也是在前期開發中踩過的坑,照著“所見即所得”的模式,像富文本編輯器一樣,輸入修改完就是最終輸出的內容,也未嘗不可,實現時習慣使用 jQuery 的伙伴很容易會聯想到直接操作 DOM,比如一個元素的定位,使用 jquery-ui 的 draggble 拖拖拽拽很方便的定位,最后產出的就是最后實際的HTML。但放在實際場景中后,會發現拓展性兼容性不太友好。特別是在后期再去操作一段成品的 DOM 結構會變得比較麻煩,比如一個定位的數據,成品中的數據會看起來比較“死”,在適配不同屏幕時,計算對應的值會比較累。而如果是操作數據的話,可以在渲染之前對數據進行些處理,最后的產出就變得比較靈活,將數據層和視圖層抽離的比較獨立,拓展起來也比較容易。
映射關系
如何把這些界面的業務抽象成數據操作,首先還是簡單分析整理下。一個可視化編輯應用的操作有很多,這里只舉幾個類型的數據操作。用戶通過操作(比如輸入、拖拽、移動、點擊等)來改變元素的屬性值。
用腦圖發散一下有哪些功能:
頁面的增刪改查
元素的增刪改查
歷史記錄的操作
用戶操作
其他
讓我們回到已經制定的目標上,如何編輯頁面,如何編輯元素。下面舉幾個例子
頁面編輯
一個H5由多個頁面組成,由幾個{元素}組成的[元素集合],此類關系通常可以用數組來表示。
將頁面集合簡單成抽象成數據的操作:
+-------------+ | | +-------------+ => pages: [], index: -1
新增頁面時,在 pages 數組中 push 一個"page 1"的實例對象,再通過索引取到該實例數據, 然后通過渲染方法將對應的視圖渲染到界面中,這個關系鏈就基本完成了。
+-------------+ | page 1 | +-------------+ => pages: [ page1 ], index: 0
交換頁面順序
+-------------+ | page 2 | +-------------+ | page 1 | +-------------+ => pages: [ page2, page1 ], index: 1
通過數組的兩個值的順序交換即可以實現兩個頁面的順序交換,發現很多場景只需要通過一些數組最基本的操作就可以實現一些看起來復雜的功能,而困難的更多是如何找到這一層映射的關系。
元素操作
元素有多種屬性組成,多個{屬性鍵值對}組成的{元素},此類關系通常可以用對象鍵值對來表示。
在元素對象上不斷拓展需要變化的屬性,比如元素的尺寸位置:
element: { style: { "top", "left", "width", "height", }, ... }
可以設計如上圖四個輸入框,每個輸入框對應每一個屬性值,這樣一個簡單的元素屬性編輯控件就好了,依次類推,每加一個可編輯屬性就對應加一個編輯控件。基本上都是以key-value的形式來操作。整個過程簡化成用戶通過界面的輸入修改操作數據,數據更改后視圖對應重新渲染一遍。
根據不斷的嘗試和增加,最后結構變成了類似如下的格式:
element: { id: 1, role: { type, value }, style: { "top", "left", "width", "height", "transform", ... }, inner: { html: "rich text", style:{ "background-image", "background-color", "background-size", "opacity", "color", "font-size", "text-align", "border-radius", ... } }, attribute:{ "animation-sequence", } } +--------+--------+----------+---------+-------------+ | id | role | style | inner | attribute | +--------+--------+----------+---------+-------------+ | 1 | link | ... | ... | ... | +--------+--------+----------+---------+-------------+ | 2 | text | ... | ... | ... | +--------+--------+----------+---------+-------------+
完善后,一個元素的結構已經變得相對龐大了,包含了非常多的屬性,隨之而來的也是非常多對應的屬性編輯控件,也是相對比較復雜的地方。
歷史操作
怎么抽象設計?這在平時業務場景里并不多。先分析下歷史要什么?主要就是撤銷和恢復,用戶可以 ctrl+z 回到上一個狀態。歷史這個大集合里肯定有多個歷史狀態,由多個{歷史}組成[歷史集合],于是就想到了數組。新狀態和老狀態的區別是什么?可能就是有了新的操作,數據有了變化,那么把這時的數據保存起來,塞到歷史里,相當于是一個 push 的操作,看起來可行。再假如需要回到上一個狀態,可以設置個索引 index, 將 index 指向到前一個,就拿到了前一個狀態。
| -- push +------v------+ index --> | status 3 | +-------------+ | status 2 | +-------------+ | status 1 | +------|------+ v -- shift
抽取幾個關鍵點:
- 有多個狀態 -> 數組 - 不同狀態之間指向 -> 數組的索引值, 游標 - 可以做個步數限制 -> 數組的長度
場景:有一個新的操作,即將新的數據插入到歷史中
history.push(statusNew);
場景:如果滿了,將最先插入的數據拿出來
history.shift();
場景:撤銷一步,將游標指向到前一個,取到前一個狀態。重做一步同理。根據這時的數據重新渲染,那么界面上也就回到了前一步的狀態。
cursor --; callback(history[cursor]);
那么history的結構就可能長成如下:
[ {status1}, {status2}, {status3}, {statusNew}, ]
這樣一個簡單的歷史數據結構設計就完成了。
留個問題: 如果撤回到了上幾步,然后繼續操作,整個歷史狀態該怎么處理?
最后最后再通過組裝整合,一個可視化編輯器主要的功能大致就滿足了。再重新看下操作界面上的數據,可以劃分為兩個部分,一個前臺頁面數據,一個后臺交互數據,大致如下:
回顧上面的過程,已經從一個簡單的數據列表渲染到具有前后臺復雜型數據交互的WebIDE,但從數據結構的設計形式上看,本質上變化其實并不是很大,只是
上述的過程在開發其他項目時同樣適用,在開始設計時,要一下子腦補出整個設計是比較困難的,特別是對某一個事物從一無所知到有點概念,從0到1的過程,客觀的說這并不容易。可以先試著抽離出幾個關鍵步驟,寫幾個小模塊,把關鍵路徑走通,在初期十分有效,隨后再這些看似零散的小組件拼裝起來,往往這個雛形會比一開始想的清晰很多,如此反復,整個設計也會變得更加清晰飽滿。數據的設計也是相對應的,由一個個小的數據組成,漸漸的便會形成一個比較龐大的數據,這時代碼可能不是最關鍵的,而是如何合理有效清晰地管理這些數據,可能更像是后端數據庫管理一樣。往往需要經過不斷的試錯走些歪路的過程,最后會慢慢得心應手一點。好設計是不斷迭代出來的,勇敢試錯,不怕踩坑,有句話叫,坑踩的深才銘心刻骨。
iKcamp原創新書《移動Web前端高效開發實戰》已在亞馬遜、京東、當當開售。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/85080.html
摘要:發送請求,處理數據。在上面這個場景中,這類數據的結構可能是最常碰到的。整個過程可以簡化成數據的變化引起視圖的變化,和現在很多前端框架數據驅動思想有幾分相似。至此一個對于頁面的抽象出來的數據結構雛形基本完成了。 作者:周周(滬江資深Web前端開發工程師)本文為原創文章,轉載請注明作者及出處 前言 近期在小D十周年活動之際,又看到了一個自家H5專題夢工廠生成的頁面。 showImg(htt...
摘要:滬江網校現在的架構是怎么樣的基于以上原則,在搭建架構的時候,經過討論和嘗試,我們最終確定出個方向,模塊化,組件化,工程化,規范化。 作者: 未來本文轉自互聯網技術聯盟(ITA1024)技術分享實錄 正文如下 沒有統一架構的時候是怎樣的一種情況? 起初前端是沒有架構的,大家只是在完成一個一個的頁面。我們來看看會發生什么。 A同事是一個非常有意思的人,他喜歡把跟這個頁面相關的所有的JS都...
摘要:原文地址一個非常適合入門學習的博客項目前端掘金一個非常適合入門學習的項目,代碼清晰結構合理新聞前端掘金介紹一個由編寫的新聞。深入淺出讀書筆記知乎專欄前端專欄前端掘金去年的一篇老文章,恰好今天專欄開通,遷移過來。 破解前端面試(80% 應聘者不及格系列):從閉包說起 - 掘金修訂說明:發布《80% 應聘者都不及格的 JS 面試題》之后,全網閱讀量超過 6W,在知乎、掘金、cnodejs ...
閱讀 3476·2021-11-19 09:40
閱讀 1492·2021-10-13 09:41
閱讀 2655·2021-09-29 09:35
閱讀 2710·2021-09-23 11:21
閱讀 1693·2021-09-09 11:56
閱讀 830·2019-08-30 15:53
閱讀 844·2019-08-30 15:52
閱讀 598·2019-08-30 12:47