为了账号安全,请及时绑定邮箱和手机立即绑定

smox 2.0:react 状态管理顶级设计

2019.01.08 22:08 803浏览

图片描述
唔,大家好呀,我是 132,那个,我又来了::>_<::

smox 是我去年这个时候写的 react 状态管理库,从最初的 redux 替代品,到 react 版本的 vuex,一直在进步

直到前阵子,突然来了新的灵感,这次 smox 2.0,不再仅仅是进步品这么简单,内部实现除了还保留发布订阅机制,其他的实现已经不一样了。

可以这么理解,redux 是 flux、elm 的进步实现,smox 却是 redux 的进步实现

废话不多说,上 API

Use

const state = {
  count: 2
}

const actions = {
  up(state, data) {
    state.count += data
  },
  down(state, data) {
    state.count -= data
  }
}

const effects = {
  async upAsync(actions, data) {
    await new Promise(t => setTimeout(t, 1000))
    actions.up(data)
  }
}

const store = new Store({ state, actions, effects })

如你所见,actions 和 effects 都是对象,里面存放的是函数,参数为他们要触发的对象

看似没什么不一样的地方,但是同样的 API 实现,我却冥思了一天

Nexted

这里面最重要的是 path 机制,我称之为 nexted-proxy

actions 和 effects 接受的参数,是被限制作用域的,什么意思?

const state = {
  counter:{
    count: 2
  }
}

const actions = {
  counter: {
    up(state, data) {
      state.count += data
    },
    down(state, data) {
      state.count -= data
    }
  }
}

@map({
  state:['counter/count'],
  actions:['counter/up','counter/down']
})

也就是说,可以通过包裹对象的形式,进行 store 拆分,smox 会自动将 key 作为 path 不断往下传递,上面的例子中,actions 接收的 state 就是 counter 对象下的 state

这个机制是 smox 最成功的一个机制了,对比 rematch、dva 的 model 机制更灵活强大,设计上也更精彩√

Proxy

immed 是我写的一个小库,可以理解为 immer 的迷你实现

它的作用和 immer 一样,可以将对象 copy 一份,来保证不可变

同时,使用 Object.defineproperty 对 IE 进行兼容,我好贴心::>_<::

架构和 API 设计

在 smox 中,actions 的方法能够不去 return,是 immed 的功劳,不去 return 会让这个 API 变得好看太多了

至于 effects,它其实一层副作用的包裹,通常用来异步执行 action

通过 async/await ,也可以不去回调了(then 方法除外),这样以来,这个 API 也变得完美了

如此,smox 的架构变得非常清晰:

state <- actions <- effects 形成闭环,完美的架构设计

以上,再无其他。

可能你会问,dispatch 去哪儿了?reducer 去哪儿了?action.type 去哪儿了?

当然是没了。

redux 设计 dispatch 是为了保证 action 触发的唯一性,也就是 action 必须由 dispatch 触发,最终保证的是 state 只由 action 而改变

smox 不需要 dispatch 也可以做到,因为 smox 的 actions 是封装过函数,state 最终同样可以保证 state 只通过 action 改变

所以,对比 redux,smox 的设计中,移除了 dispatch,reducer 这种概念

同样的作为进步品的 rematch、dva 等,是没有变化的,也就是没有改变 redux 的本质

smox-react

看完了 smox,再来看 smox-react

@map({
  state: ['count'],
  actions: ['up', 'down'],
  effects: ['upAsync']
})
class Counter extends React.Component {
  render() {
    return (
      <div>
        <div>{this.props.count}</div>
        <div>{this.props.sex}</div>
        <button onClick={() => this.props.up(1)}>+</button>
        <button onClick={() => this.props.down(1)}>-</button>
        <button onClick={() => this.props.upAsync(1)}>异步</button>
      </div>
    )
  }
}

如上,最重要的是 map 这个 API,大家可还曾记得 connect?

我随便从网上找了个 dva 的 connect 长这样:

@connect(({ user, login, global = {}, loading }) => ({
  currentUser: user.currentUser,
  collapsed: global.collapsed,
  fetchingNotices: loading.effects['global/fetchNotices'],
  notices: global.notices,
  menuData: login.menuData,
  redirectData: login.redirectData
}))

这可能是我……学习 react 见过的最可怕的 API 没有之一了::>_<::

没有对比就没有伤害,smox 的 map 其实抄自 vuex 的 mapXxx,以字符串数组的形式去遍历,然后筛选出对应的 state 和 函数,注入到组件中

这个 API 我也封装到极致了可以说::>_<::

smox-react 还有一处比较讨喜,就是,不再需要 bindActionCreators ,毕竟已经不再需要 dispatch 了嘛

总结

smox 2.0 是我年前最棒的一次重构了,标题写个最佳应该不过分,至今没有发现能把 API 封装到 smox 这个程度的√

对比同为进步品的 rematch、dva 等,除了 API 更加好看,还放弃了 redux 原有的机制,是创造,不是封装√

这一切的一切,仅仅只有 1kb

最后,放上 smox 的 github 地址:

欢迎试用与 star !

点击查看更多内容
1人点赞

若觉得本文不错,就分享一下吧!

评论

相关文章推荐

正在加载中
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消