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