开发复杂的应用时,不可避免会有一些数据相互引用。建议你尽可能地把 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'
}
}
}
});
上面是不是觉得简单了?那么给你一个比较复杂的情形,万变不离其宗。我们最终想抽离出 articles
、users
以及 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
}
}
}