唔,大家好呀,我是 132,那个,我又来了::>_<::
smox 是我去年这个时候写的 react 状态管理库,从最初的 redux 替代品,到 react 版本的 vuex,一直在进步
直到前阵子,突然来了新的灵感,这次 smox 2.0,不再仅仅是进步品这么简单,内部实现除了还保留发布订阅机制,其他的实现已经不一样了。
可以这么理解,redux 是 flux、elm 的进步实现,smox 却是 redux 的进步实现
废话不多说,上 API
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 实现,我却冥思了一天
这里面最重要的是 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 机制更灵活强大,设计上也更精彩√
immed 是我写的一个小库,可以理解为 immer 的迷你实现
它的作用和 immer 一样,可以将对象 copy 一份,来保证不可变
同时,使用 Object.defineproperty
对 IE 进行兼容,我好贴心::>_<::
在 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,再来看 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 地址:
https://github.com/132yse/smox
欢迎试用与 star !