[译]JSON数据范式化(normalizr)

jopen 8年前

开发复杂的应用时,不可避免会有一些数据相互引用。建议你尽可能地把 state 范式化,不存在嵌套。把所有数据放到一个对象里,每个数据以 ID 为主键,不同数据相互引用时通过 ID 来查找。把  应用的 state 想像成数据库 。这种方法在  normalizr 文档里有详细阐述。

normalizr :将嵌套的JSON格式扁平化,方便被Redux利用;

目标

我们的目标是将:

[{    id: 1,    title: 'Some Article',    author: {      id: 1,      name: 'Dan'    }  }, {    id: 2,    title: 'Other Article',    author: {      id: 1,      name: 'Dan'    }  }]
  • 数组的每个对象都糅合了三个维度  文章  、  作者
  • 按照数据范式,应当将这两个维度拆分出来,两者的联系通过id关联起来即可

我们描述上述的结构: - 返回的是一个数组(array) - 数组的对象中包含另外一个schema —— user

应该比较合理的,应该是转换成:

{    result: [1, 2],    entities: {      articles: {        1: {          id: 1,          title: 'Some Article',          author: 1        },        2: {          id: 2,          title: 'Other Article',          author: 1        }      },      users: {        1: {          id: 1,          name: 'Dan'        }      }    }  }

如何使用

观看示例最好的,就是官方的测试文件: https://github.com/gaearon/normalizr/blob/master/test/index.js

先引入 normalizr

import { normalize, Schema, arrayOf } from 'normalizr';  

定义 schema

    var article = new Schema('articles'),          user = new Schema('users'),          collection = new Schema('collections'),          feedSchema,          input;

定义规则:

    article.define({        author: user,        collections: arrayOf(collection)      });        collection.define({        curator: user      });        feedSchema = {        feed: arrayOf(article)      };

测试:

    input = {        feed: [{          id: 1,          title: 'Some Article',          author: {            id: 3,            name: 'Mike Persson'          },          collections: [{            id: 1,            title: 'Awesome Writing',            curator: {              id: 4,              name: 'Andy Warhol'            }          }, {            id: 7,            title: 'Even Awesomer',            curator: {              id: 100,              name: 'T.S. Eliot'            }          }]        }, {          id: 2,          title: 'Other Article',          collections: [{            id: 2,            title: 'Neverhood',            curator: {              id: 120,              name: 'Ada Lovelace'            }          }],          author: {            id: 2,            name: 'Pete Hunt'          }        }]      };        Object.freeze(input);        normalize(input, feedSchema).should.eql({        result: {          feed: [1, 2]        },        entities: {          articles: {            1: {              id: 1,              title: 'Some Article',              author: 3,              collections: [1, 7]            },            2: {              id: 2,              title: 'Other Article',              author: 2,              collections: [2]            }          },          collections: {            1: {              id: 1,              title: 'Awesome Writing',              curator: 4            },            2: {              id: 2,              title: 'Neverhood',              curator: 120            },            7: {              id: 7,              title: 'Even Awesomer',              curator: 100            }          },          users: {            2: {              id: 2,              name: 'Pete Hunt'            },            3: {              id: 3,              name: 'Mike Persson'            },            4: {              id: 4,              name: 'Andy Warhol'            },            100: {              id: 100,              name: 'T.S. Eliot'            },            120: {              id: 120,              name: 'Ada Lovelace'            }          }        }      });

优势

假定请求 /articles 返回的数据的schema如下:

articles: article*    article: {      author: user,    likers: user*    primary_collection: collection?    collections: collection*  }    collection: {      curator: user  }

如果不做范式化,store需要事先知道API的各种结构,比如 UserStore 会包含很多样板代码来获取新用户,诸如下面那样:

  switch (action.type) {    case ActionTypes.RECEIVE_USERS:      mergeUsers(action.rawUsers);      break;      case ActionTypes.RECEIVE_ARTICLES:      action.rawArticles.forEach(rawArticle => {        mergeUsers([rawArticle.user]);        mergeUsers(rawArticle.likers);          mergeUsers([rawArticle.primaryCollection.curator]);        rawArticle.collections.forEach(rawCollection => {          mergeUsers(rawCollection.curator);        });      });        UserStore.emitChange();      break;    }

store表示累觉不爱啊!! 每个store都要对返回的 进行各种foreach 才能获取想要的数据。

来一个范式吧:

const article = new Schema('articles');    const user = new Schema('users');    article.define({      author: user,    contributors: arrayOf(user),    meta: {      likes: arrayOf({        user: user      })    }  });    // ...    const json = getArticleArray();    const normalized = normalize(json, arrayOf(article));  

经过范式整顿之后,你爱理或者不爱理,users对象总是在 action.entities.users 中:

  const { action } = payload;      if (action.response && action.response.entities && action.response.entities.users) {      mergeUsers(action.response.entities.users);      UserStore.emitChange();      break;    }

更多示例(来自测试文件)

规范化单个文件

    var article = new Schema('articles'),          input;        input = {        id: 1,        title: 'Some Article',        isFavorite: false      };        Object.freeze(input);        normalize(input, article).should.eql({        result: 1,        entities: {          articles: {            1: {              id: 1,              title: 'Some Article',              isFavorite: false            }          }        }      });

规范化内嵌对象,并删除额外key

有时候后端接口会返回很多额外的字段,甚至会有重复的字段;比如下方示例中 typeId 和  type.id 是重复的;注意方法中 形参 key 是经过 artcle.define 定义过的。

    var article = new Schema('articles'),          type = new Schema('types'),          input;        // 定义内嵌规则      article.define({        type: type      });        input = {        id: 1,        title: 'Some Article',        isFavorite: false,        typeId: 1,        type: {          id: 1,        }      };        Object.freeze(input);        // assignEntity删除后端返回额外数据的      var options = {        assignEntity: function(obj, key, val) {          obj[key] = val;          delete obj[key + 'Id'];        }      };        normalize(input, article, options).should.eql({        result: 1,        entities: {          articles: {            1: {              id: 1,              title: 'Some Article',              isFavorite: false,              type: 1            }          },          types: {            1: {              id: 1            }          }        }      });

添加额外数据

和上个示例相反的是, mergeIntoEntity 用于将多份同质数据不同信息融合到一起,用于解决冲突。

下方示例中, author 和  reviewer 是同一个人,只是前者留下的联系方式是手机,后者留下的联系方式是邮箱,但无论如何都是同一个人;

此时就可以使用 mergeIntoEntity 将两份数据融合到一起;(注意这里是用  valueOf 规则 )

    var author = new Schema('authors'),          input;        input = {        author: {          id: 1,          name: 'Ada Lovelace',          contact: {            phone: '555-0100'          }        },        reviewer: {          id: 1,          name: 'Ada Lovelace',          contact: {            email: 'ada@lovelace.com'          }        }      }        Object.freeze(input);        var options = {        mergeIntoEntity: function(entityA, entityB, entityKey) {          var key;            for (key in entityB) {            if (!entityB.hasOwnProperty(key)) {              continue;            }              if (!entityA.hasOwnProperty(key) || isEqual(entityA[key], entityB[key])) {              entityA[key] = entityB[key];              continue;            }              if (isObject(entityA[key]) && isObject(entityB[key])) {              merge(entityA[key], entityB[key])              continue;            }              console.warn('Unequal data!');          }        }      };        normalize(input, valuesOf(author), options).should.eql({        result: {          author: 1,          reviewer: 1        },        entities: {          authors: {            1: {              id: 1,              name: 'Ada Lovelace',              contact: {                phone: '555-0100',                email: 'ada@lovelace.com'              }            }          }        }      });

按指定的属性规范化

有时候对象没有 id 属性,或者我们并不想按  id 属性规范化,可以使用  idAttribute 指定;

下面的例子,就是使用 slug 作为规范化的key:

  var article = new Schema('articles', { idAttribute: 'slug' }),          input;        input = {        id: 1,        slug: 'some-article',        title: 'Some Article',        isFavorite: false      };        Object.freeze(input);        normalize(input, article).should.eql({        result: 'some-article',        entities: {          articles: {            'some-article': {              id: 1,              slug: 'some-article',              title: 'Some Article',              isFavorite: false            }          }        }      });

创建自定义的属性

有时候想自己创建一个key,虽然今天和去年创建的文章名称都是 Happy ,但明显是不一样的,为了按时间区分出来,可以  使用自定义函数生成想要的key 。

    function makeSlug(article) {        var posted = article.posted,            title = article.title.toLowerCase().replace(' ', '-');          return [title, posted.year, posted.month, posted.day].join('-');      }        var article = new Schema('articles', { idAttribute: makeSlug }),          input;        input = {        id: 1,        title: 'Some Article',        isFavorite: false,        posted: {          day: 12,          month: 3,          year: 1983        }      };        Object.freeze(input);        normalize(input, article).should.eql({        result: 'some-article-1983-3-12',        entities: {          articles: {            'some-article-1983-3-12': {              id: 1,              title: 'Some Article',              isFavorite: false,              posted: {                day: 12,                month: 3,                year: 1983              }            }          }        }      });

规范化数组

后端返回的数据往往是一串数组居多,此时规范化起到很大的作用,规范化的同时将数据压缩了一遍;

   var article = new Schema('articles'),          input;        input = [{        id: 1,        title: 'Some Article'      }, {        id: 2,        title: 'Other Article'      }];        Object.freeze(input);        normalize(input, arrayOf(article)).should.eql({        result: [1, 2],        entities: {          articles: {            1: {              id: 1,              title: 'Some Article'            },            2: {              id: 2,              title: 'Other Article'            }          }        }      });

抽取多个schema

上面讲的情形比较简单,只涉及抽出结果是单个schema的情形;现实中,你往往想抽象出多个schema,比如下方,我想抽离出 tutorials (教程) 和 articles (文章)两个 schema,此时需要  通过 schemaAttribute 选项指定区分这两个 schema 的字段 :

    var article = new Schema('articles'),          tutorial = new Schema('tutorials'),          articleOrTutorial = { articles: article, tutorials: tutorial },          input;        input = [{        id: 1,        type: 'articles',        title: 'Some Article'      }, {        id: 1,        type: 'tutorials',        title: 'Some Tutorial'      }];        Object.freeze(input);        normalize(input, arrayOf(articleOrTutorial, { schemaAttribute: 'type' })).should.eql({        result: [          {id: 1, schema: 'articles'},          {id: 1, schema: 'tutorials'}        ],        entities: {          articles: {            1: {              id: 1,              type: 'articles',              title: 'Some Article'            }          },          tutorials: {            1: {              id: 1,              type: 'tutorials',              title: 'Some Tutorial'            }          }        }      });

这个示例中,虽然文章的id都是1,但很明显它们是不同的文章,因为一篇是普通文章,一篇是教程文章;因此要按schema维度抽离数据;

这里的 arrayOf(articleOrTutorial) 中的  articleOrTutorial 是包含多个属性的对象,这表示  input 应该是  articleOrTutorial 中的一种情况;

有时候原始数据属性 和 我们定义的有些差别,此时可以将 schemaAttribute 的值设成函数,将原始属性经过适当加工;比如原始属性是 tutorial , 而抽离出的 schema 名字为  tutorials ,相差一个 s

    function guessSchema(item) {        return item.type + 's';      }        var article = new Schema('articles'),          tutorial = new Schema('tutorials'),          articleOrTutorial = { articles: article, tutorials: tutorial },          input;        input = [{        id: 1,        type: 'article',        title: 'Some Article'      }, {        id: 1,        type: 'tutorial',        title: 'Some Tutorial'      }];        Object.freeze(input);        normalize(input, arrayOf(articleOrTutorial, { schemaAttribute: guessSchema })).should.eql({        result: [          { id: 1, schema: 'articles' },          { id: 1, schema: 'tutorials' }        ],        entities: {          articles: {            1: {              id: 1,              type: 'article',              title: 'Some Article'            }          },          tutorials: {            1: {              id: 1,              type: 'tutorial',              title: 'Some Tutorial'            }          }        }      });

上述是数组情况,针对普通的对象也是可以的,将规则 改成 valueOf 即可:

   var article = new Schema('articles'),          tutorial = new Schema('tutorials'),          articleOrTutorial = { articles: article, tutorials: tutorial },          input;        input = {        one: {          id: 1,          type: 'articles',          title: 'Some Article'        },        two: {          id: 2,          type: 'articles',          title: 'Another Article'        },        three: {          id: 1,          type: 'tutorials',          title: 'Some Tutorial'        }      };        Object.freeze(input);        normalize(input, valuesOf(articleOrTutorial, { schemaAttribute: 'type' })).should.eql({        result: {          one: {id: 1, schema: 'articles'},          two: {id: 2, schema: 'articles'},          three: {id: 1, schema: 'tutorials'}        },        entities: {          articles: {            1: {              id: 1,              type: 'articles',              title: 'Some Article'            },            2: {              id: 2,              type: 'articles',              title: 'Another Article'            }          },          tutorials: {            1: {              id: 1,              type: 'tutorials',              title: 'Some Tutorial'            }          }        }      });

schemaAttribute 是函数的情况就不列举了,和上述一致;

规范化内嵌情形

上面的对象比较简单,原本就是扁平化的;如果对象格式稍微复杂一些,比如每篇文章有多个作者的情形。此时需要使用 define 事先声明 schema 之间的层级关系:

    var article = new Schema('articles'),          user = new Schema('users'),          input;        article.define({        author: user      });        input = {        id: 1,        title: 'Some Article',        author: {          id: 3,          name: 'Mike Persson'        }      };        Object.freeze(input);        normalize(input, article).should.eql({        result: 1,        entities: {          articles: {            1: {              id: 1,              title: 'Some Article',              author: 3            }          },          users: {            3: {              id: 3,              name: 'Mike Persson'            }          }        }      });

上面是不是觉得简单了?那么给你一个比较复杂的情形,万变不离其宗。我们最终想抽离出 articlesusers 以及  collections 这三个 schema,所以只要定义这三个schema就行了,

然后使用 define 方法声明这三个schema之间千丝万缕的关系;

最外层的feed只是属性,并不需要定义;

    var article = new Schema('articles'),          user = new Schema('users'),          collection = new Schema('collections'),          feedSchema,          input;        article.define({        author: user,        collections: arrayOf(collection)      });        collection.define({        curator: user      });        feedSchema = {        feed: arrayOf(article)      };        input = {        feed: [{          id: 1,          title: 'Some Article',          author: {            id: 3,            name: 'Mike Persson'          },          collections: [{            id: 1,            title: 'Awesome Writing',            curator: {              id: 4,              name: 'Andy Warhol'            }          }, {            id: 7,            title: 'Even Awesomer',            curator: {              id: 100,              name: 'T.S. Eliot'            }          }]        }, {          id: 2,          title: 'Other Article',          collections: [{            id: 2,            title: 'Neverhood',            curator: {              id: 120,              name: 'Ada Lovelace'            }          }],          author: {            id: 2,            name: 'Pete Hunt'          }        }]      };        Object.freeze(input);        normalize(input, feedSchema).should.eql({        result: {          feed: [1, 2]        },        entities: {          articles: {            1: {              id: 1,              title: 'Some Article',              author: 3,              collections: [1, 7]            },            2: {              id: 2,              title: 'Other Article',              author: 2,              collections: [2]            }          },          collections: {            1: {              id: 1,              title: 'Awesome Writing',              curator: 4            },            2: {              id: 2,              title: 'Neverhood',              curator: 120            },            7: {              id: 7,              title: 'Even Awesomer',              curator: 100            }          },          users: {            2: {              id: 2,              name: 'Pete Hunt'            },            3: {              id: 3,              name: 'Mike Persson'            },            4: {              id: 4,              name: 'Andy Warhol'            },            100: {              id: 100,              name: 'T.S. Eliot'            },            120: {              id: 120,              name: 'Ada Lovelace'            }          }        }      });

内嵌+数组倾斜

   var article = new Schema('articles'),          tutorial = new Schema('tutorials'),          articleOrTutorial = { articles: article, tutorials: tutorial },          user = new Schema('users'),          collection = new Schema('collections'),          feedSchema,          input;        article.define({        author: user,        collections: arrayOf(collection)      });        tutorial.define({        author: user,        collections: arrayOf(collection)      });        collection.define({        curator: user      });        feedSchema = {        feed: arrayOf(articleOrTutorial, { schemaAttribute: 'type' })      };        input = {        feed: [{          id: 1,          type: 'articles',          title: 'Some Article',          author: {            id: 3,            name: 'Mike Persson'          },          collections: [{            id: 1,            title: 'Awesome Writing',            curator: {              id: 4,              name: 'Andy Warhol'            }          }, {            id: 7,            title: 'Even Awesomer',            curator: {              id: 100,              name: 'T.S. Eliot'            }          }]        }, {          id: 1,          type: 'tutorials',          title: 'Some Tutorial',          collections: [{            id: 2,            title: 'Neverhood',            curator: {              id: 120,              name: 'Ada Lovelace'            }          }],          author: {            id: 2,            name: 'Pete Hunt'          }        }]      };        Object.freeze(input);        normalize(input, feedSchema).should.eql({        result: {          feed: [            { id: 1, schema: 'articles' },            { id: 1, schema: 'tutorials' }          ]        },        entities: {          articles: {            1: {              id: 1,              type: 'articles',              title: 'Some Article',              author: 3,              collections: [1, 7]            }          },          tutorials: {            1: {              id: 1,              type: 'tutorials',              title: 'Some Tutorial',              author: 2,              collections: [2]            }          },          collections: {            1: {              id: 1,              title: 'Awesome Writing',              curator: 4            },            2: {              id: 2,              title: 'Neverhood',              curator: 120            },            7: {              id: 7,              title: 'Even Awesomer',              curator: 100            }          },          users: {            2: {              id: 2,              name: 'Pete Hunt'            },            3: {              id: 3,              name: 'Mike Persson'            },            4: {              id: 4,              name: 'Andy Warhol'            },            100: {              id: 100,              name: 'T.S. Eliot'            },            120: {              id: 120,              name: 'Ada Lovelace'            }          }        }      });

内嵌 + 对象(再内嵌)

看到下面的 valuesOf(arrayOf(user)) 了没有,它表示该属性是一个对象,对象里面各个数组值是 User对象数组;

    var article = new Schema('articles'),          user = new Schema('users'),          feedSchema,          input;        article.define({        collaborators: valuesOf(arrayOf(user))      });        feedSchema = {        feed: arrayOf(article),        suggestions: valuesOf(arrayOf(article))      };        input = {        feed: [{          id: 1,          title: 'Some Article',          collaborators: {            authors: [{              id: 3,              name: 'Mike Persson'            }],            reviewers: [{              id: 2,              name: 'Pete Hunt'            }]          }        }, {          id: 2,          title: 'Other Article',          collaborators: {            authors: [{              id: 2,              name: 'Pete Hunt'            }]          }        }, {          id: 3,          title: 'Last Article'        }],        suggestions: {          1: [{            id: 2,            title: 'Other Article',            collaborators: {              authors: [{                id: 2,                name: 'Pete Hunt'              }]            }          }, {            id: 3,            title: 'Last Article'          }]        }      };        Object.freeze(input);        normalize(input, feedSchema).should.eql({        result: {          feed: [1, 2, 3],          suggestions: {            1: [2, 3]          }        },        entities: {          articles: {            1: {              id: 1,              title: 'Some Article',              collaborators: {                authors: [3],                reviewers: [2]              }            },            2: {              id: 2,              title: 'Other Article',              collaborators: {                authors: [2]              }            },            3: {              id: 3,              title: 'Last Article'            }          },          users: {            2: {              id: 2,              name: 'Pete Hunt'            },            3: {              id: 3,              name: 'Mike Persson'            }          }        }      });

还有更加复杂的,这次用上 valuesOf(userOrGroup, { schemaAttribute: 'type' }) 了:

    var article = new Schema('articles'),          user = new Schema('users'),          group = new Schema('groups'),          userOrGroup = { users: user, groups: group },          feedSchema,          input;        article.define({        collaborators: valuesOf(userOrGroup, { schemaAttribute: 'type' })      });        feedSchema = {        feed: arrayOf(article),        suggestions: valuesOf(arrayOf(article))      };        input = {        feed: [{          id: 1,          title: 'Some Article',          collaborators: {            author: {              id: 3,              type: 'users',              name: 'Mike Persson'            },            reviewer: {              id: 2,              type: 'groups',              name: 'Reviewer Group'            }          }        }, {          id: 2,          title: 'Other Article',          collaborators: {            author: {              id: 2,              type: 'users',              name: 'Pete Hunt'            }          }        }, {          id: 3,          title: 'Last Article'        }],        suggestions: {          1: [{            id: 2,            title: 'Other Article'          }, {            id: 3,            title: 'Last Article'          }]        }      };        Object.freeze(input);        normalize(input, feedSchema).should.eql({        result: {          feed: [1, 2, 3],          suggestions: {            1: [2, 3]          }        },        entities: {          articles: {            1: {              id: 1,              title: 'Some Article',              collaborators: {                author: { id: 3, schema: 'users' },                reviewer: { id: 2, schema: 'groups' }              }            },            2: {              id: 2,              title: 'Other Article',              collaborators: {                author: { id: 2, schema: 'users' }              }            },            3: {              id: 3,              title: 'Last Article'            }          },          users: {            2: {              id: 2,              type: 'users',              name: 'Pete Hunt'            },            3: {              id: 3,              type: 'users',              name: 'Mike Persson'            }          },          groups: {            2: {              id: 2,              type: 'groups',              name: 'Reviewer Group'            }          }        }      });

递归调用

比如某某人关注了另外的人, 用户 写了一系列文章,该文章  被其他用户 订阅就是这种情况:

    var article = new Schema('articles'),          user = new Schema('users'),          collection = new Schema('collections'),          feedSchema,          input;        user.define({        articles: arrayOf(article)      });        article.define({        collections: arrayOf(collection)      });        collection.define({        subscribers: arrayOf(user)      });        feedSchema = {        feed: arrayOf(article)      };        input = {        feed: [{          id: 1,          title: 'Some Article',          collections: [{            id: 1,            title: 'Awesome Writing',            subscribers: [{              id: 4,              name: 'Andy Warhol',              articles: [{                id: 1,                title: 'Some Article'              }]            }, {              id: 100,              name: 'T.S. Eliot',              articles: [{                id: 1,                title: 'Some Article'              }]            }]          }, {            id: 7,            title: 'Even Awesomer',            subscribers: [{              id: 100,              name: 'T.S. Eliot',              articles: [{                id: 1,                title: 'Some Article'              }]            }]          }]        }]      };        Object.freeze(input);        normalize(input, feedSchema).should.eql({        result: {          feed: [1]        },        entities: {          articles: {            1: {              id: 1,              title: 'Some Article',              collections: [1, 7]            }          },          collections: {            1: {              id: 1,              title: 'Awesome Writing',              subscribers: [4, 100]            },            7: {              id: 7,              title: 'Even Awesomer',              subscribers: [100]            }          },          users: {            4: {              id: 4,              name: 'Andy Warhol',              articles: [1]            },            100: {              id: 100,              name: 'T.S. Eliot',              articles: [1]            }          }        }      });

上面还算好的,有些schema直接就递归声明了,比如 儿女和父母 的关系:

    var user = new Schema('users'),          input;        user.define({        parent: user      });        input = {        id: 1,        name: 'Andy Warhol',        parent: {          id: 7,          name: 'Tom Dale',          parent: {            id: 4,            name: 'Pete Hunt'          }        }      };        Object.freeze(input);        normalize(input, user).should.eql({        result: 1,        entities: {          users: {            1: {              id: 1,              name: 'Andy Warhol',              parent: 7            },            7: {              id: 7,              name: 'Tom Dale',              parent: 4            },            4: {              id: 4,              name: 'Pete Hunt'            }          }        }      });

自动merge属性

在一个数组里面,如果id属性一致,会自动抽取并合属性成一个:

    var writer = new Schema('writers'),          book = new Schema('books'),          schema = arrayOf(writer),          input;        writer.define({        books: arrayOf(book)      });        input = [{        id: 3,        name: 'Jo Rowling',        isBritish: true,        location: {          x: 100,          y: 200,          nested: ['hello', {            world: true          }]        },        books: [{          id: 1,          soldWell: true,          name: 'Harry Potter'        }]      }, {        id: 3,        name: 'Jo Rowling',        bio: 'writer',        location: {          x: 100,          y: 200,          nested: ['hello', {            world: true          }]        },        books: [{          id: 1,          isAwesome: true,          name: 'Harry Potter'        }]      }];        normalize(input, schema).should.eql({        result: [3, 3],        entities: {          writers: {            3: {              id: 3,              isBritish: true,              name: 'Jo Rowling',              bio: 'writer',              books: [1],              location: {                x: 100,                y: 200,                nested: ['hello', {                  world: true                }]              }            }          },          books: {            1: {              id: 1,              isAwesome: true,              soldWell: true,              name: 'Harry Potter'            }          }        }      });

如果合并过程中有冲突会有提示,并自动剔除冲突的属性;比如下方同一个作者写的书,一个对象里描述“卖得好”,而在另外一个对象里却描述“卖得差”,明显是有问题的:

    var writer = new Schema('writers'),          book = new Schema('books'),          schema = arrayOf(writer),          input;        writer.define({        books: arrayOf(book)      });        input = [{        id: 3,        name: 'Jo Rowling',        books: [{          id: 1,          soldWell: true,          name: 'Harry Potter'        }]      }, {        id: 3,        name: 'Jo Rowling',        books: [{          id: 1,          soldWell: false,          name: 'Harry Potter'        }]      }];        var warnCalled = false,          realConsoleWarn;        function mockWarn() {        warnCalled = true;      }        realConsoleWarn = console.warn;      console.warn = mockWarn;        normalize(input, schema).should.eql({        result: [3, 3],        entities: {          writers: {            3: {              id: 3,              name: 'Jo Rowling',              books: [1]            }          },          books: {            1: {              id: 1,              soldWell: true,              name: 'Harry Potter'            }          }        }      });        warnCalled.should.eq(true);      console.warn = realConsoleWarn;

传入不存在的schema规范

如果应用的schma规范不存在,你还传入,就会创建一个新的父属性:

    var writer = new Schema('writers'),          schema = writer,          input;      input = {        id: 'constructor',        name: 'Constructor',        isAwesome: true      };        normalize(input, schema).should.eql({        result: 'constructor',        entities: {          writers: {            constructor: {              id: 'constructor',              name: 'Constructor',              isAwesome: true            }          }        }
</div>

来自: https://yq.aliyun.com/articles/3168