作者曾经开发过一个数据分享的小程序。分享逻辑类似于百度网盘。当前数据可以被分享者处理后再继续分享(你可以控制数据的过期时间,是否可以处理数据并继续分享)。共享数据是一个深度嵌套的json对象。用户读取共享数据时,存储在小程序云数据库中(共享数据不同于业务数据,不使用业务服务器进行维护)。如果我们直接存储数据,云数据库很快就会变得非常大。其次,我们没有办法分析和检索共享者的子数据。这时候需要进行数据转换,进行拆分和维护。我们可以使用redux作者DanAbramov编写的normalizr来处理数据。normalizr的初衷是处理深度复杂的嵌套对象。如何使用官方稍微修改过的例子,假设获取到如下书籍数据:{id:"1",title:"JavaScript从入门到放弃",//authorauthor:{id:"1",name:"chc"},//评论comments:[{id:"1",content:"作者写的真好",commenter:{id:"1",name:"chc"}},{id:"2",content:"楼上假数据",commenter:{id:"2",name:"dcd"}},]}这时候我们可以写3个主题:图书信息、评论和用户。我们先从基础数据构造schema:import{normalize,schema}from'normalizr';//构造第一个实体用户信息constuser=newschema.Entity('users');//构造第二个实体Commentconstcomment=newschema.Entity('comments',{//评论者为用户commenter:user});//构造第三个实体bookconstbook=newschema.Entity('books',{//Authorauthor:user,//评论comments:[comment]});//传入的数据和当前最大的schema信息constnormalizedData=normalize(originalData,book);我们先来看看最终的数据。{“实体”:{“用户”:{“1”:{“id”:“1”,“名称”:“chc”},“2”:{“id”:“2”,“名称”:"dcd"}},"comments":{"1":{"id":"1","content":"作者写的真好","commenter":"1"},"2":{"id":"2","content":"楼上假数据","commenter":"2"}},"books":{"1":{"id":"1","title":"JavaScript从入门到放弃","author":"1","comments":["1","2"]}}},"result":"1"}去掉其他信息,我们可以可以看到得到了3个不同的实体对象,users、comments、books对象的key是当前id,value是当前tile的数据结构。这时候我们可以使用对象或者数组(Object.values)来添加和更新数据。看到这里的分析逻辑,你可能会很疑惑。先不管代码实现,这里分析一下库是如何解析我们写的schema的,方便大家在实际场景中使用,再看看data和schema的定义:datastructure{id:"1",title:"JavaScript从入门到放弃",//作者author:{id:"1",name:"chc"},//评论comments:[{id:"1",content:"作者写的toowell",commenter:{id:"1",name:"chc"}},{id:"2",content:"楼上的假数据",commenter:{id:"2",name:"dcd"}},]}图书信息为第一层对象,数据包含id、书名、作者、评论,对应的schema如下constbook=newschema.Entity('books',{//authorauthor:user,//一本书对应多条A评论,所以数组comments:[comment]});其中id和title是书本身的属性,不用注意,把需要解析的数据结构写出来。books字符串与解析无关,它对应于entities对象的键。查看userconstuser=newschema.Entity('users');用户没有信息需要解析,直接定义实体即可。最后是评论信息constcomment=newschema.Entity('comments',{//评论者是用户commenter:user});{id:"1",content:"作者写的真好",commenter:{id:"1",name:"chc"}}把注释去掉原来的数据结构,其实很清楚。高级用法处理数组normalizr可以解析单个对象,那么如果当前业务传递的是数组呢?类似于注释,可以直接这样使用:[{id:'1',title:"JavaScript从入门到放弃"//...},{id:'2',//...}]constnormalizedData=normalize(originalData,[book]);在逆向分析中,我们只需要获取刚才normalizedData中的result和entities,就可以得到之前的信息。import{denormalize,schema}from'normalizr';//...denormalize(normalizedData.result,book,normalizedData.entities);实体配置开发可以根据配置信息重新解析实体数据。constbook=newschema.Entity('books',{//authorauthor:user,//一本书对应多个评论,所以这里使用数组comments:[comment]},{//默认主键是id,否则使用idAttribute中的数据,如cid,key等idAttribute:'id',//预处理策略,参数为实体的输入值,父对象)=>value,//遇到两个相同id的数据的合并策略默认如下,我们可以继续修改mergeStrategy:(prev,prev)=>({...prev,...next,//是否已经合并,如果相同则添加该属性isMerge:true}),});//看一个更复杂的例子,以user为例Entity('users',{},{processStrategy:(value,parent,key)=>{//增加父对象的属性//例如commenter:"1"=>commenterId:"1"orauthor:"2"=>"authorId":"2"//但目前还不可能通过delete删除commenter或author属性parent[`${key}Id`]=value.id//如果用户信息是从评论中获取的,则添加commentIds属性if(key==='commenter'){return{。..value,commentIds:[parent.id]}}//不要忘记返回值,否则不会生成用户数据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}}当然,这个库还可以进行更复杂的数据格式化,大家可以通过api文档进一步学习和使用其他的,normalizr的使用场景毕竟有限,里面的人开源的责任已经改变。目前主库已不再维护(该issue也已关闭)。当然,normalizr代码本身已经足够稳定了。作者也在考虑一些新的场景,尝试给normalizr增加一些新的功能(比如id转换)和优化(ts重构)。如果大家在使用normalizr的过程中遇到什么问题,也可以联系我,库目前在normalizr-helper中。鼓励如果您觉得这篇文章不错,希望您能给我一些鼓励,帮我在我的github博客下star。博客地址
