簡介: 數據模型對於常規的數據查詢或填寫數據提交,是否有使用場景或者價值?數據模型這條路走的是否有問題?前端
Vmo 是我在 18 年發佈的一個工具庫,用於快速建立數據模型,當時我寫了一篇文章《Vmo 前端數據模型設計》獲得過一段時間的關注,當時我從事三維裝修相關的項目。在圖形學的背景基礎及海量複雜的數據的狀況下,天然而然在前端則會衍生出一種數據處理、解析、消費的技術方案,也種下了我對數據模型概念的種子。後端
簡單舉個例子:須要解析一個三維裝修的房子的數據會有哪些呢?數組
在解析這些數據中存在很是多的相互關聯和計算,好比 房間須要和牆面,牆面須要和牆體關聯,牆體和最多 2 個房間關聯,牆角和多個房間關聯,牆角和多個牆體關聯等等瀏覽器
面對這樣海量、複雜的數據,若是隻靠着一個 API 請求的結果消費顯然是很是不可取的方案,先不說這些數據能不能正確的解析出來,就說這些數據如何維護,保存時如何收集到全部數據反向序列化給後端都是些頭疼的問題。安全
固然這些問題在當時咱們抽象的各個數據模型中獲得瞭解決,若是想了解具體細節能夠查看我以前的文章。數據結構
今天我想講的是,在我加入阿里後,一直在思考的關於數據模型的兩個問題:框架
在尋覓了 2 年後,主導 Lazada 商家端的商品發佈頁面重構時,彷彿找到了一些答案。函數
首先在新增一個商品的過程當中,其實是用戶在以客戶端的形式製做一組商品數據,常規的前端視角來看就是提交一份「JSON」。工具
而編輯就是經過 API 拉取這份「JSON」解析到 Form 表單中,讓用戶進行編輯後,再將這份「JSON」提交。單元測試
那麼粗略的將數據抽象爲模型將會成爲這樣:
Well,到目前爲止,咱們作的事情都感受像是在脫褲子放屁,畫蛇添足。哈哈哈,各位看官暫且勿噴,稍安勿躁 。
那麼爲何須要把這些數據抽象爲一個類呢?我拿一下幾個 Case 來講明:
1 、請求數據 & 單元測試
不少時候,前端把對數據的請求和處理是寫在組件中的,更優一點可能會封裝在某個聚類裏面,或者某個 Hook 裏面,調用時輕巧的拿到狀態和數據。
像商品這樣的數據請求方式會存在多種:草稿中獲取,編輯中獲取,某個類目中獲取(不一樣類目下,商品屬性不一樣)。
每種獲取方式請求的接口和參數組合方式可能不一樣,但最後前端消費的產物倒是相同的。按照策略模式來講,對於一個商品模型的獲取只是使用了不一樣的策略,但產物倒是一致的,消費端不管調用何種方式,獲取到的結果都是可靠的 Product 模型類。
有經驗的前端都知道,不少時候,在一個項目在一輪輪的迭代後,咱們的接口數據每每會存在部分數據須要前端作必定處理或者轉換。
面對這樣的數據處理,若是放在一個組件或者 Hook 中,是不太合適的,在作單元測試或者數據消費的時候均可能會給咱們帶來一些阻力。
在我看來,調試一個數據問題最好的辦法,就是寫一個單元測試,對單元測試預期的結果進行調試,每每比咱們在瀏覽器中 Mock 一份數據調試數據更高效,對未來的穩定性也更有幫助。
安全感,數據消費起來,一個類和一份 JSON 給開發者帶來的安全感和爽感是徹底不一樣的。消費過數據模型 或者 次一點 消費過Interface的小夥伴,我相信對這一點是很是認同的。
哈哈,說到這裏有些小夥伴可能要問了,你說的這個咱們用Interface也能達到一樣的效果呀。好,我們繼續...
二、 計算性消費數據
什麼叫計算性消費數據的,說的簡單點,就好比:
class Person1 { fistName = "Wang"; lastName = "Yee"; get fullName() { return `${this.lastName} ${this.fistName}`; // Yee Wang } get fullNameCN() { return `${this.fistName} ${this.lastName}`; // Wang Yee } }
上面這個例子很是經典且清晰,元數據中可能只是些基本數據,可是不少時候前端須要根據不一樣場景來進行元數據組裝,以往這些數據每每會被封裝爲各個方法,或者被當作 template 寫在組件中,散落在各個角落,每當用到這份數據時可能又會從新按照場景組裝一遍。每每這種時候就會存在 需求缺失,好比某狀況下須要將以前全部消費到 fullName 的地方改成小寫。
拿到商品發佈來講,計算性消費數據到底有哪些應用場景呢?
在此以前,我想先解釋一下SKU這個數據模型,它其中最核心的元數據是:
按照上圖這個表格中所示,能夠看到該商品共有 6 個 SKU,第一個 SKU 所對應的SKU模型數據應爲:
class SKU { value = new Map([ [ new SKUProperty({ id: 1, label: "Color Family" }), new SKUValue({ id: 101, label: "Red" }), ], [ new SKUProperty({ id: 2, label: "Size" }), new SKUValue({ id: 201, label: "33" }), ], ]); price: string; }
像這樣一個 SKU Model,它所具有的元數據已經能夠清晰描述當前 SKU,並且能夠經過 SKU 的擴展方法作到不少有用的數據,好比:
相比與只是一個 Object 對象來講,數據模型可以帶來很是多的數據處理和數據擴展能力,當某種狀況下須要消費由該數據產生的計算性消費數據時,能夠很輕易的進行擴展使用,對於數據結構也有更好的預期和掌控力。
結合對該數據模型的單元測試,就能夠清晰快速的開發數據層,當數據層可靠後,在視圖層消費就會變得行雲流水,駕輕就熟了。
舉個單元測試的例子:
it("alias sku equal", () => { const data = [ { text: "300MB", value: 2988, name: "p-1", }, { text: "Blue", value: 2888, alias: "Blue1", name: "p-2", }, ]; const sku = SKU.fromData(data); expect( sku.isEqual( SKU.fromData([ { text: "300MB", value: 2988, name: "p-1", }, { text: "Blue", value: 2888, alias: "Blue2", name: "p-2", }, ]) ) ).toBeFalsy(); });
這種SKU,是一種類型較爲特殊的SKU,它其中會存在 alias 字段,當有這種字段時,在作SKU比對時,不但要對 SKUProperty,SKUValue 的ID作比對,還須要對 alias 字段作比對。
因此按照上面的單測來看,結果應該是 false,由於這兩份數據中的alias是不一樣的。沒辦法,這是一個業務需求。
若是在視圖層作數據比對時,使用的是純數據進行比對,頗有可能漏掉這部分邏輯,這就會致使項目變得捉襟見肘,拆東牆補西牆。
反正,在消費層遇到不少的須要對數據處理或判斷時,大能夠將這部分能力交給數據模型來處理,由數據模型來保證數據的穩定性。
三、 數據關係
使用數據模型,還能夠幫你清晰管理數據關係,好比商品和SKU之間,SKU和 SKUProperty,SKUValue 之間的關係。
我舉個具體案例:
這是一個商品編輯時組 笛卡爾積(Cartesian product) 的過程,當咱們的SKU屬性被用戶添加或者修改時,將會觸發笛卡爾積的從新計算出最新的排列組合結果。
好比當用戶新增一個尺碼爲35時,笛卡爾積將會多出兩項組合結果。同理,若是當維度增長一列時,好比添加材質維度,將會產生更多SKU結果。
以往,前端開發者總會將這部分計算過程封裝成爲一個數學方法,放在utils中隨時調用,這看起沒什麼問題。
若是將這個過程看作是,一個 SKUCollection 數據模型的構建過程的話,一切就會將變得瓜熟蒂落:
test('sku calculate whether valid', () => { const skuCellection = SKUCollection.fromData({ 'p-3xxxx': [ { text: '300MB', value: 2, }, { text: '128GB', value: 3, }, ], 'p-4xxxx': [ { text: 'Blue', value: 3, }, { text: 'Red', value: 15, }, { text: 'Green', value: 1, }, ], }); expect( skuCellection.value ).toEqual( // 6 SKU Model ); });
有了這樣一個數據模型結構後,就能夠清晰的經過數據模型來調用其相關的數據和計算性數據。
另外,不一樣的數據模型雖然相互依賴,但對數據解析和計算性數據缺相互獨立,能夠作到獨立使用和單元測試。
商品發佈本質上是一個較爲複雜的表單提交頁面。因爲字段多,交互複雜等緣由,在產品設計過程當中,就已經將不少字段先拆分爲不一樣模塊,來減輕用戶心理負擔。
好比會存在:基礎信息,商品屬性,詳描,運費等。
在填寫過程當中,會存在部分 前端校驗 + 後端校驗 的場景。
在數據提交或者其餘數據寫入過程當中,後端同時會處理字段校驗,當後端發現某個字段填寫錯誤時,服務端將返回錯誤信息及錯誤字段信息。
爲了更好的交互體驗,前端將會根據返回獲取到字段信息,定位到對應的字段位置,顯示錯誤信息並報紅,另外還須要根據當前字段判斷其所歸屬的模塊進行報錯。
還有一種狀況是:服務端的第一層校驗經過,調用其餘商品上游鏈路時拋出異常,此時上游鏈路可能已經丟失字段信息,面對這樣的異常數據,前端須要展現在表單頂部,而且提供traceId,以便追蹤定位異常。
這樣的異常數據,一般處理都須要和後端反覆確認不一樣Case的表現狀況,有些異常甚至很難出現一次,咱們在迭代過程當中每每會由於一些組件變更或者邏輯變更丟失這部分數據消費能力。
就商品發佈來講,顯而易見的"保存"的動做是一個須要處理異常的狀況,因此咱們會在提交的地方寫上不少後端返回異常時的處理邏輯。
當有一天,有另一個迭代須要寫入操做時,一樣也會產生異常的狀況,這些的異常狀況再次處理時又會有不少數據轉換和錯誤顯示的邏輯。
若是收到這份後端返回數據,將他轉換爲異常數據模型,而後交由視圖層消費,這樣會讓全部異常模型下須要處理的邏輯複用避免交互邏輯丟失。
固然,視圖層如何更巧妙的消費該數據模型又是另一個有意思的設計,此處暫且不表,後面我還會寫一篇專門介紹商品發佈的視圖層狀態管理設計。
在商品發佈中,除了上述提到的幾個數據模型之外,其實還構建了一些其餘類型的數據模型,如:運費模型,商品質量分模型,類目推薦模型等... 而後由這些多個子模型共同組合成爲一個商品的模型。
這樣的數據模型在消費起來,開發者其實不會太過關心究竟須要請求什麼API,返回的數據到底是什麼樣的,他們的返回是否要處理、轉換、兼容等問題。
同時,這樣高質量的數據模型其實不依賴於視圖層的框架,它能夠被抽離做爲一個獨立的包來管理維護,而後在其餘頁面引入使用,好比商品域可能遇到的:商品管理,商品選擇,運費編輯,商品質量分預覽等等...
回到開頭,我提到的問題:
首先確定的是,在我所使用的過程當中,數據模型確實很是清晰,有力,牢固的解決了我所面到的業務問題,因此它是有價值的。
至於和常規的需求,到底應該用什麼好呢?哈哈,這個問題有個比較無賴的回答,小孩子才考慮什麼要什麼不要,成年人什麼都要,沒有什麼技術是非黑即白的。
Vite 就只能在 Vue 的項目裏面使用嗎?
什麼合適用什麼,簡單的數據查詢展現不須要這麼精細的數據處理,固然能夠直接拿來即用咯,解決業務問題的方法就是好方法!
至於Composition API,其實在商品發佈的重構過程當中,基本絕大多數都是使用這種設計思路來實現的,這樣的設計確實能讓咱們清晰的分辨每一個方法是幹什麼的,是否會影響交互,以及這樣的交互是在作什麼,每一個交互都在一個位置維護和處理,後面我會單獨寫一篇介紹。
實踐過程當中發現,數據模型和Composition API並不衝突,一個是用來處理數據層,一個是用來處理視圖層,它們相輔相成結合一些訂閱模式的設計,就會讓整個項目的劃分異常清晰,我十分建議你們在之後遇到單點項目較爲複雜時可以使用這一套思路來解決業務問題!
做者:開發者小助手_LS
原文連接本文爲阿里雲原創內容,未經容許不得轉載