Redux vs Mobx系列(二):衍生属性
Redux vs Mobx系列(二):衍生属性考虑这样得一个页面 其中 在设计数据层的时候, 我们可以: var store ={ price: 0,count: 0,money: 0 } 这样 我们的组件 就可以直接从 store里面获取price, count, money,然后展示就可以了,很方便简单,当更新的时候: function updatePrice(newPrice,oldStore){ const price = newPrcie const money = price * oldStore.count return { price,count: oldStore.count,money,} } function updateCount(newCount,oldStore){ const count = newCount const money = count * oldStore.price return { price: oldStore.price count,} } 现在,我们业务复杂了: 如果store还是设计如下: var store = { inprice: '',outprice: '',... inmoney: '',outmoney: '',... taxmoney: '',// 含税金额 ... grossmargin: '',// 毛利率 } 页面组件逻辑依然很简单,获取对应数据展示就可以了。 问题来了,现在我要调整一下 售价 function updateInprice(newInprice,oldStore) { const inprice = newInprice const inmoney = inprice * oldStore.count // update 含税金额, 税额, 毛利, 毛利率 .... const grossmargin = .... } waht ??我调整一个售价, 需要改这么多??是的, 当您 调整数量, 调整进价, 调整报损量。。。都需要改这么多!!! 还记得[一]()里面mobx的性能隐患吗。
。。。 迫在眉睫!我们必须尽可能的减少维护的状态, 一旦状态足够少,我们就更容易的保证了数据层的正确 那么根据 已上面的例子来看,其中 成本金额, 销售金额... 税额...毛利率 这几个状态都不需要我们管理, 因为从已知的状态 完全可以推导出来, 比如: 现在我们在来看之前的这个例子: /// store var store = { inprice: '',... tax: '' } /// update function updateInprice(newInprice,store) { store.inprice = newInprice } /// get compute date function getInmoney(store){ return store.inprice * store.count } ... function getGrossmargin(store) { return (outprice * (count - 报损量) - inprice * count) / inprice * count } 现在 状态有12个减少到6个, 而且互相独立, 这样更新也很简单(如代码)。 页面在展示的时候 只需要从store获取数据, 然后调用get方法 获取衍生数据就可以了。 “老板, 我回家陪老婆了。” “好嘞!” 对于数据交互越复杂的应用(注意是 数据交互越复杂), 框架对衍生属性的处理就非常重要了。 mobxmobx对衍生属性处理的很好, class OrderLine { @observable price = 0; @observable amount = 1; constructor(price) { this.price = price; } @computed get total() { return this.price * this.amount; } } 这里的total就是衍生属性 摘录mobx官方文档的一句话:`如果任何影响计算值的值发生变化了,计算值将根据状态自动进行衍生。 计算值在大多数情况下可以被 MobX 优化的,因为它们被认为是纯函数。 例如,如果前一个计算中使用的数据没有更改,计算属性将不会重新运行 @observer class X extends Component { componentDidMount(){ setInternal(() => { this.forceUpdate() },1000) } render() { const gm = this.props.grossmargin // 衍生属性 毛利率 ... } } 在上面这种render不断的执行情况下(通过forceUpdate)触发, grossmargin并不会重新计算,而是重复使用上一次缓存的值,直到影响grossmargin的状态改变。 不过还是有两个地方需要注意
import { observable,computed,autorun } from "mobx"; class OrderLine { @observable price = 3; @observable amount = 1; @computed get total() { console.log('invoke total') if (this.price > 6) { return 5 * this.amount } return this.price * this.amount; } } const ol = new OrderLine() ol.price = 5 /*autorun(() => { // autorun console.log('xxxx:',ol.total) })*/ console.log('xxxx:',ol.total) console.log('xxxx:',ol.total) 按照之前的说法, 这里虽然多次引用了 reduxredux本身并没有提供对衍生属性的处理。 function mapStateToProps(state) { const { inprice,outprice,tax ... } = state const inmoney = getInmoney(state) const grossmargin = getGrossmargin(state) .... return { inprice,tax,... inmoney,grossmargin,... } } redux的通知是 粗粒度的, 也就是说每当有store发生改变的时候, 所有在页面上
我们需要精确的控制 衍生属性的处理。 第三方库reselect是做这个事情的, import { createSelector } from reselect const inmoneySelect = createSelector( state => state.inprice state => state.count (inprice,count) => getInmoney(inpirce,count) ) reselect 会重复利用缓存结果, 直到相关的属性修改。 reselect写起来有点繁琐。 我们这里使用repure 来替代reselect。 import repure from 'repure' function getInmoney(inprice,count) { .... } const reGetInmoney = repure(getInmoney) // 给getInmoney增加缓存的功能 function getGrossmargin(inprice,count,outprice....) { ... } const reGetGrossmargin(getGrossmargin) //给getGrossmargin增加缓存的功能 ... function mapStateToProps(state) { const { inprice,tax ... } = state const inmoney = reGetInmoney(inpirce,count) const grossmargin = reGetGrossmargin(inprice,outprice....) .... return { inprice,... } } repure比reselect书写更加简单自然, 我们就是在写普通的方法, 然后repure一下,让其具有缓存的功能。 具体请看reselect的替代者repure 不管是reselect还是 repure都很高效 end用好衍生属性会让我们的应用简单很多。 mobx天生支持, 写法简单自然。 不过正如本文所说, 有些隐藏的坑。redux本身没有提供方法, 但是有很多第三方库提供了处理, 也很高效。 其中repure的写法是比较简单自然的。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |