JavaScript利用normalizr實現(xiàn)復(fù)雜數(shù)據(jù)轉(zhuǎn)換
筆者曾經(jīng)開發(fā)過一個數(shù)據(jù)分享類的小程序,分享邏輯上類似于百度網(wǎng)盤。當前數(shù)據(jù)可以由被分享者加工然后繼續(xù)分享(可以控制數(shù)據(jù)的過期時間、是否可以加工數(shù)據(jù)以及繼續(xù)分享)。
分享的數(shù)據(jù)是一個深度嵌套的 json 對象。在用戶讀取分享數(shù)據(jù)時存入小程序云數(shù)據(jù)庫中(分享的數(shù)據(jù)和業(yè)務(wù)數(shù)據(jù)有差異,沒使用業(yè)務(wù)服務(wù)器進行維護)。如果拿到數(shù)據(jù)就直接存儲的話,很快云數(shù)據(jù)庫就會變得很大,其次我們也沒辦法分析各項和檢索各項子數(shù)據(jù)給予分享者。
這時候需要進行數(shù)據(jù)轉(zhuǎn)換以便拆分和維護。我們可以使用 redux 作者 Dan Abramov 編寫的 normalizr 來處理數(shù)據(jù)。
normalizr 創(chuàng)立的初衷是處理深層,復(fù)雜的嵌套的對象。
如何使用
稍微修改一下官方的例子,假定獲取到如下書籍的數(shù)據(jù):
{ id: "1", title: "JavaScript 從入門到放棄", // 作者 author: { id: "1", name: "chc" }, // 評論 comments: [ { id: "1", content: "作者寫的太好了", commenter: { id: "1", name: "chc" } }, { id: "2", content: "樓上造假數(shù)據(jù)哈", commenter: { id: "2", name: "dcd" } }, ] }
這時候我們可以寫出 3 個主體: 書籍信息、評論以及用戶。我們先從基礎(chǔ)的數(shù)據(jù)來構(gòu)造模式:
import { normalize, schema } from 'normalizr'; // 構(gòu)造第一個實體 用戶信息 const user = new schema.Entity('users'); // 構(gòu)造第二個實體 評論 const comment = new schema.Entity('comments', { // 評價者是用戶 commenter: user }); // 構(gòu)造第三個實體 書籍 const book = new schema.Entity('books', { // 作者 author: user, // 評論 comments: [comment] }); // 傳入數(shù)據(jù)以及當前最大的 schema 信息 const normalizedData = normalize(originalData, book);
先來看一下最終數(shù)據(jù)。
{ "entities": { "users": { "1": { "id": "1", "name": "chc" }, "2": { "id": "2", "name": "dcd" } }, "comments": { "1": { "id": "1", "content": "作者寫的太好了", "commenter": "1" }, "2": { "id": "2", "content": "樓上造假數(shù)據(jù)哈", "commenter": "2" } }, "books": { "1": { "id": "1", "title": "JavaScript 從入門到放棄", "author": "1", "comments": [ "1", "2" ] } } }, "result": "1" }
去除其他信息,我們可以看到獲取了 3 個不同的實體對象, users,comments,books。對象的鍵為當前 id,值為當前平鋪的數(shù)據(jù)結(jié)構(gòu)。這時候我們就可以使用對象或者數(shù)組(Object.values) 來新增和更新數(shù)據(jù)。
解析邏輯
看到這里,大家可能是很懵的。先不管代碼實現(xiàn),這里先分析一下庫是如何解析我們編寫的 schema 的,以便大家可以在實際場景中使用,再看一遍數(shù)據(jù)和 schema 定義:
數(shù)據(jù)結(jié)構(gòu)
{ id: "1", title: "JavaScript 從入門到放棄", // 作者 author: { id: "1", name: "chc" }, // 評論 comments: [ { id: "1", content: "作者寫的太好了", commenter: { id: "1", name: "chc" } }, { id: "2", content: "樓上造假數(shù)據(jù)哈", commenter: { id: "2", name: "dcd" } }, ] }
書籍信息是第一層對象,數(shù)據(jù)中有 id, title, author, comments,對應(yīng) schema 如下
const book = new schema.Entity('books', { // 作者 author: user, // 一本書對應(yīng)多個評論,所以這里使用數(shù)組 comments: [comment] });
其中 id ,title 是 book 本身的屬性,無需關(guān)注,把需要解析的數(shù)據(jù)結(jié)構(gòu)寫出來。books 字符串與解析無關(guān),對應(yīng) entities 對象的 key。
再看 user
const user = new schema.Entity('users');
user 沒有需要解析的信息,直接定義實體即可。
最后是評論信息
const comment = new schema.Entity('comments', { // 評價者是用戶 commenter: user }); { id: "1", content: "作者寫的太好了", commenter: { id: "1", name: "chc" } }
把 comments 從原本的數(shù)據(jù)結(jié)構(gòu)中拿出來,實際也就很清晰了。
高階用法
處理數(shù)組
normalizr 可以解析單個對象,那么如果當前業(yè)務(wù)傳遞數(shù)組呢?類似于 comment 直接這樣使用即可:
[ { id: '1', title: "JavaScript 從入門到放棄" // ... }, { id: '2', // ... } ] const normalizedData = normalize(originalData, [book]);
反向解析
我們只需要拿到剛才的 normalizedData 中的 result 以及 entities 就可以獲取之前的信息了。
import { denormalize, schema } from 'normalizr'; //... denormalize(normalizedData.result, book, normalizedData.entities);
Entity 配置
開發(fā)中可以根據(jù)配置信息重新解析實體數(shù)據(jù)。
const book = new schema.Entity('books', { // 作者 author: user, // 一本書對應(yīng)多個評論,所以這里使用數(shù)組 comments: [comment] }, { // 默認主鍵為 id,否則使用 idAttribute 中的數(shù)據(jù),如 cid,key 等 idAttribute: 'id', // 預(yù)處理策略, 參數(shù)分別為 實體的輸入值, 父對象 processStrategy: (value, parent, key) => value, // 遇到兩個id 相同數(shù)據(jù)的合并策略,默認如下所示,我們還可以繼續(xù)修改 mergeStrategy: (prev, prev) => ({ ...prev, ...next, // 是否合并過,如果遇到相同的,就會添加該屬性 isMerge: true }), }); // 看一下比較復(fù)雜的例子,以 user 為例子 const user = new schema.Entity('users', { }, { processStrategy: (value, parent, key) => { // 增加父對象的屬性 // 例如 commenter: "1" => commenterId: "1" 或者 author: "2" => "authorId": "2" // 但是目前還無法通過 delete 刪除 commenter 或者 author 屬性 parent[`${key}Id`] = value.id // 如果是從評論中獲取的用戶信息就增加 commentIds 屬性 if (key === 'commenter') { return { ...value, commentIds: [parent.id] } } // 不要忘記返回 value, 否則不會生成 user 數(shù)據(jù) return { ...value, bookIds: [parent.id] }; } mergeStrategy: (prev, prev) => ({ ...prev, ...next, // 該用戶所有的評論歸并到一起去 commentIds: [...prev.commentIds, ...next.commentIds], // 該用戶所有的書本歸并到一起去 bookIds: [...prev.bookIds, ...next.bookIds], isMerge: true }), }) // 最終獲取的用戶信息為 { "1": { "id": "1", "name": "chc" // 用戶 chc 寫了評論和書籍,但是沒有進行過合并 "commentIds": ["1"], "bookIds": ["1"], }, "2": { "id": "2", "name": "dcd", // 用戶 dcd 寫了 2 個評論,同時進行了合并處理 "commentIds": [ "2", "3" ], "isMerge": true } }
當然了,該庫也可以進行更加復(fù)雜的數(shù)據(jù)格式化,大家可以通過 api 文檔 來進一步學(xué)習(xí)和使用。
其他
當然了,normalizr 使用場景畢竟有限,開源負責(zé)人也早已換人。目前主庫已經(jīng)無人維護了(issue 也也已經(jīng)關(guān)閉)。當然了,normalizr 代碼本身也是足夠穩(wěn)定。
筆者也在考慮一些新的場景使用并嘗試為 normalizr 添加一些新的功能(如 id 轉(zhuǎn)換)和優(yōu)化(ts 重構(gòu)),如果您在使用 normalizr 的過程中遇到什么問題,也可以聯(lián)系我,存儲庫目前在 normalizr-helper 中。
到此這篇關(guān)于JavaScript利用normalizr實現(xiàn)復(fù)雜數(shù)據(jù)轉(zhuǎn)換的文章就介紹到這了,更多相關(guān)JavaScript normalizr復(fù)雜數(shù)據(jù)轉(zhuǎn)換內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
javascript實現(xiàn)粘貼qq截圖功能(clipboardData)
這篇文章主要介紹了javascript實現(xiàn)粘貼qq截圖功能,利用clipboardData在網(wǎng)頁中實現(xiàn)截屏粘貼的功能,感興趣的小伙伴們可以參考一下2016-05-05優(yōu)雅而高效的JavaScript?try...catch語句詳解(js異常處理)
這篇文章主要給大家介紹了關(guān)于JavaScript中try...catch語句的相關(guān)資料,也就是js異常處理方法,try...catch是JavaScript中的錯誤處理機制,它的作用是捕獲和處理可能發(fā)生的錯誤,以避免程序崩潰,需要的朋友可以參考下2024-01-01