关于react.js:React多页面数据缓存方案

背景

业务模块离开,整体业务流由多个单页面组成,如:还款途中,须要绑卡,设置交易明码,或者还须要批改手机号码,最初一系列操作后回到还款页面提交还款,整个流程在多个页面间跳转,单页面的数据无奈失去保留,故须要一套多页面的数据缓存计划。

数据保留

思考浏览器的个别通用性,将数据缓存在sessionStorage中。

旧计划

每次触发页面跳转时,手动调用window.sessionStorage.setItem保留须要保留的数据,在页面跳转回来的时候,读取sessionStorage中的数据,将其赋给须要笼罩的变量,而后触发对应的初始化操作。现有这种计划存在以下一些有余:

  • 每个页面都须要编写storeData和restoreData的逻辑,扩散的逻辑不易治理;
  • 如果援用了组件,组件本身保护了state的话,子组件须要被缓存的数据须要裸露给父组件能力对立在父组件中触发storeData逻辑,而restoreData的失去的数据也须要传递到子组件能力进行相应的赋值操作,同时要思考组件渲染的机会,以及数据申请回来的机会之间的先后顺序

优化点

  • 须要一个对立的数据保留对象
  • 须要一个对立的数据管理入口
  • 对立的数据保留对象是为了,跳转前的间接缓存这个对象,后续跳转回来,也间接读取这个对象,并且全局都能拜访到这个对象,即能优化旧计划的第二个点,缩小父子组件间的数据传输;
  • 对立的数据管理入口,是为了方便管理数据缓存的逻辑,同时防止反复写代码;

改良计划

全局对象GlobalStore

  • 针对上述的几点,首先是定义一个globalStore的对象,而后export进去,各个须要缓存的页面就间接import这个对象。
  • 同时为了防止不同页面间的命名净化,须要给每个页面赋予一个pageId,并设为globalstore的key值,故最终GlobalStore格局如:GlobalStorepageId = value。
  • 如何标记须要被缓存的数据?思考个别波及展现的都是state中的数据,故间接将state保留下来,另外组件的props个别会变动的也是由父组件的state传递下来的,所以props不须要额定保留,故最终选取保留的数据是state里的数据。

数据管理入口

  • 因为业务代码中会批改state的状态,在最初页面跳转时,如何同步以后的state是须要解决的问题,通过实例this能够获取以后的state,但如果以后页面用到子组件,子组件中的state也须要被缓存,那也须要获取子组件的实例,从而获取他的state,这就会使数据管理很艰难,整体缓存脉络也很不清晰,所以思路是是否能在更改state的同时,同步更新全局的GlobalStore;
  • 一开始是想要采取相似vue的作法,利用Object.definedproperty,写一个set的拦截器,但起初发现setstate并不是简略的通过this.state[key] = newValue来批改数据,而是整体替换,所以这个计划行不通;
  • 第二个计划就是间接改写React.component原型上的setState,在其上增加同步更新GlobalStore的代码,但并不是每个页面都须要缓存,改写原型会使其余不须要缓存的页面页回去批改GlobalStore,当然这里也能够依据以后实例的pageId来判断是否要写缓存来躲避这个问题,但本着尽量不扭转react逻辑的准则,采纳了另一种计划;
  • 应用装璜器,也就是高阶组件,能够实现反向代理,同时对业务代码的侵入性更小,只须要在每个用到的页面的类上加一个装璜器并传入pageid作为参数就能够开启缓存策略;
  • 另外利用反向代理,能够获取到父类state上的所有属性,通过super.render()能够进行渲染,并通过给子类增加setState属性,笼罩原型上的setState办法,同时在增加的setState办法上调用super.setState来保证数据能失去正确更新,以及触发视图渲染;
  • 而后在增加的setState上获取到更新的对象,写入GlobalStore上,然而思考到setState除了能够传入一个对象进行更新,还能够传入一个函数进行更新,而函数会承受前一个状态的state和props为参数,而如果同时调用了两次setState,尽管时批量更新,state不会马上批改值,但后一次setState中的preState是前一个setState批改后的state后果,这种逻辑下,要获取正确的state对象会比较复杂且容易出错,所以须要对传入的数据进行一下包装,再setState中申明一个函数,以arguments作为入参,外部执行一遍传入的func,入参仍旧是arguments,记录返回值,剖析更新了哪个key,对应批改进GlobalStore,最初返回这个返回值,并把这个外部申明的函数传入super.setState中,这样外部执行批改办法时也会批改到GlobalStore对象;
  • 至此利用装璜器反向代理,咱们实现了在constructor阶段,将数据回填笼罩state,同时用本人实现的setState拦挡react.component原型上的setState实现数据同步,最初每次产生内部跳转的时候,调用一个通用的跳转办法,在跳转的同时,将GlobalStore写入sessionStorage,这样回来初始化页面调用构造函数时就能够回填数据,整个多页面数据缓存的计划就大体如此。

优化

思考理论的利用场景,还有以下几个中央能够做优化:

公共字段

  • 外部页面之间有可能应用了雷同的变量,此时没必要针对每个页面ID都保留一份正本,而应该把这部分数据提取到一个公共的字段下,例如 [common];
  • 那么就须要有伎俩去辨认出,state上的哪些字段是取自公共字段的,这里思考用一个自定义的class来实现这个性能,因为首先class能够利用instanceOf判断是否属于公共字段,其次个别没有人会在state上设置一个class的实例;
  • 当初暂且称这个class为CommonData,思考公共字段要用到的属性,给他两个属性,别离是key和value,其中key为对应GlobalStore中的字段名(因为state中的字段名不肯定要和公共字段名雷同),value为其初始值;
  • 同时对外export一个办法,暂称genCommonData,传入两个参数,并在外部实例化CommonData,返回实例;
  • 这时,装璜器就能够对state中的属性进行判断,哪些是公共字段,哪些是页面独有的字段,在constructor里,就能够进行别离解决,页面独有的字段就如之前那样解决,公共字段要用实例中的value以及GlobalStore中的值进行从新赋值,同时用一个公有变量保留state中公共字段和GlobalStore中公共字段的对应关系;
  • 依据这个对应关系,在后续setState中,会判断其是否为公共字段,从而决定更新[pageId]里的属性,还是[common]中的属性

组件数据缓存

  • 组件的数据缓存也能够用下面的一套逻辑,但思考到一个组件有可能被多个页面利用,同时还有jsx写法中难以间接应用装璜器,在定义组件的文件中,输入组件的时候,针对会应用缓存的组件,革新时应该额定输入多一种被装璜器装璜的组件作为缓存组件应用;
  • 另外,还是因为组件可能被多页面援用,这里寄存在GlobalStore中的数据不能再间接增加一个组件Id做辨别,思考到组件是挂在页面下的,能够在GlobalStore[pageId]下保留一个[componentId]的字段去进行保留,而公共字段仍然保留在[common]中;
  • 因为渲染的个性,父组件的constructor会在子组件的constructor之前执行,同时一个工夫只可能有一个页面,那么能够在GlobalStore中增加一个currentPageId的属性,去记录以后的页面id,等对应子组件加载时,间接利用currentPageId去对应的字段下赋值,从而让对应页面的数据记录在对应页面下

至此,一个根本能运行的计划就实现了

有待解决的问题

  • 一个页面下援用了多个雷同的组件,怎么保留数据? – 利用key
  • 组件嵌套的状况下怎么保留数据? – 只能利用Props指明关系?
  • 多个装璜器时可能有副作用
  • 并不是所有的state都须要被缓存的 – 参考CommonData的做法?
  • 有些须要缓存的数据不是放在state上的,而是间接挂在this上的 – 倡议放到state里,或者同样时采纳CommonData的做法
  • hook是否能够利用?

该计划对Vue的启发

  • vue多页面也会有同样的问题,vue中无奈应用装璜器,但Object.definedPorperty能够利用,在其勾子函数created和beforeMount之间,能够对批改其data上的set办法,使得数据能够同步更新,入口放在mixin;
  • 至于公共对象,因为vue会针对data中的值进行监听,不能采纳CommonData的做法,这种能够当时在页面定义一个公共字段的map,传入data时应用解构,批改监听的时候针对这个map做筛选哪些是更新到公共字段;
  • 但更间接的计划是应用Vuex,间接缓存store对象,进入页面进行init的时候就对其进行回填即可。

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理