天天看點

[譯]JSON資料範式化(normalizr)

開發複雜的應用時,不可避免會有一些資料互相引用。建議你盡可能地把 <code>state</code> 範式化,不存在嵌套。把所有資料放到一個對象裡,每個資料以 id 為主鍵,不同資料互相引用時通過 id 來查找。把 應用的 state 想像成資料庫 。這種方法在 normalizr 文檔裡有詳細闡述。

normalizr:将嵌套的json格式扁平化,友善被redux利用;

我們的目标是将:

數組的每個對象都糅合了三個次元 文章 、 作者

按照資料範式,應當将這兩個次元拆分出來,兩者的聯系通過id關聯起來即可

我們描述上述的結構: - 傳回的是一個數組(array) - 數組的對象中包含另外一個schema —— user

應該比較合理的,應該是轉換成:

觀看示例最好的,就是官方的測試檔案:https://github.com/gaearon/normalizr/blob/master/test/index.js

先引入 <code>normalizr</code>:

定義<code>schema</code>:

定義規則:

測試:

假定請求 <code>/articles</code> 傳回的資料的schema如下:

如果不做範式化,store需要事先知道api的各種結構,比如<code>userstore</code>會包含很多樣闆代碼來擷取新使用者,諸如下面那樣:

store表示累覺不愛啊!! 每個store都要對傳回的 進行各種foreach 才能擷取想要的資料。

來一個範式吧:

經過範式整頓之後,你愛理或者不愛理,users對象總是在 <code>action.entities.users</code> 中:

有時候後端接口會傳回很多額外的字段,甚至會有重複的字段;比如下方示例中 <code>typeid</code> 和 <code>type.id</code> 是重複的;注意方法中 形參<code>key</code> 是經過<code>artcle.define</code> 定義過的。

和上個示例相反的是,<code>mergeintoentity</code> 用于将多份同質資料不同資訊融合到一起,用于解決沖突。

下方示例中,<code>author</code> 和 <code>reviewer</code> 是同一個人,隻是前者留下的聯系方式是手機,後者留下的聯系方式是郵箱,但無論如何都是同一個人;

此時就可以使用 <code>mergeintoentity</code> 将兩份資料融合到一起;(注意這裡是用 <code>valueof</code>規則 )

有時候對象沒有 <code>id</code> 屬性,或者我們并不想按 <code>id</code> 屬性規範化,可以使用 idattribute 指定;

下面的例子,就是使用<code>slug</code>作為規範化的key:

有時候想自己建立一個key,雖然今天和去年建立的文章名稱都是<code>happy</code>,但明顯是不一樣的,為了按時間區分出來,可以 使用自定義函數生成想要的key 。

後端傳回的資料往往是一串數組居多,此時規範化起到很大的作用,規範化的同時将資料壓縮了一遍;

上面講的情形比較簡單,隻涉及抽出結果是單個schema的情形;現實中,你往往想抽象出多個schema,比如下方,我想抽離出 <code>tutorials</code>(教程) 和<code>articles</code>(文章)兩個 schema,此時需要 通過 schemaattribute 選項指定區分這兩個 schema 的字段 :

這個示例中,雖然文章的id都是1,但很明顯它們是不同的文章,因為一篇是普通文章,一篇是教程文章;是以要按schema次元抽離資料;

這裡的 <code>arrayof(articleortutorial)</code> 中的 <code>articleortutorial</code> 是包含多個屬性的對象,這表示 <code>input</code> 應該是 <code>articleortutorial</code> 中的一種情況;

有時候原始資料屬性 和 我們定義的有些差别,此時可以将 <code>schemaattribute</code> 的值設成函數,将原始屬性經過适當加工;比如原始屬性是<code>tutorial</code> , 而抽離出的 schema 名字為 <code>tutorials</code> ,相差一個<code>s</code>:

上述是數組情況,針對普通的對象也是可以的,将規則 改成 valueof 即可:

schemaattribute 是函數的情況就不列舉了,和上述一緻;

上面的對象比較簡單,原本就是扁平化的;如果對象格式稍微複雜一些,比如每篇文章有多個作者的情形。此時需要使用 define 事先聲明 schema 之間的層級關系:

上面是不是覺得簡單了?那麼給你一個比較複雜的情形,萬變不離其宗。我們最終想抽離出 <code>articles</code> 、<code>users</code> 以及 <code>collections</code> 這三個 schema,是以隻要定義這三個schema就行了,

然後使用 define 方法聲明這三個schema之間千絲萬縷的關系;

最外層的feed隻是屬性,并不需要定義;

看到下面的 valuesof(arrayof(user)) 了沒有,它表示該屬性是一個對象,對象裡面各個數組值是 user對象數組;

還有更加複雜的,這次用上 valuesof(userorgroup, { schemaattribute: 'type' }) 了:

比如某某人關注了另外的人,使用者 寫了一系列文章,該文章 被其他使用者 訂閱就是這種情況:

上面還算好的,有些schema直接就遞歸聲明了,比如 兒女和父母 的關系:

在一個數組裡面,如果id屬性一緻,會自動抽取并合屬性成一個:

如果合并過程中有沖突會有提示,并自動剔除沖突的屬性;比如下方同一個作者寫的書,一個對象裡描述“賣得好”,而在另外一個對象裡卻描述“賣得差”,明顯是有問題的:

如果應用的schma規範不存在,你還傳入,就會建立一個新的父屬性: