(不要忘了点赞转发关注哦Q-Q!在github点个小星星!!!
rako是一个声明式、可预测、可拓展、高内聚、简单且强大的JavaScript状态容器。
与redux不同,你的项目始终需要rako!
rako能将你的业务逻辑与视图非常好的解耦,不仅如此,因为rako鼓励模块化,你甚至可以将视图逻辑也写入rako中!控制好状态的细粒度,你的项目可维护性将会得到前所未有的提升!
github: github.com/rabbitooops…
你可以配合例子阅读下面的文章:codesandbox.io/s/011136qpk…
rako的设计源于最简单的OOP编程,虽然简单但是极具表现力。
const profile = {
name: 'Tom',
gender: 'male',
updateName(name) {
this.name = name
},
updateGender(gender) {
this.gender = gender
}
}
profile.updateName('Jerry')
复制代码
我相信所有人都能看懂上面的代码,实在是太简单了! 但是现在的功能太弱了,让我们改造一下让它变强,下面是我们想要的功能:
- 数据不可变。
- 每次update都引起一些副作用subscribe。
function profile(update) {
return {
name: 'Tom',
gender: 'male',
updateName(name) {
update({name})
},
updateGender(gender) {
update({gender})
}
}
}
const profileStore = new Store(profile)
复制代码
与直接定义profile
特别相似,只不过用了一个function包裹了profile,更新使用了传入的参数update,但是如此简单的设计配合一下rako就能迸发出强大的活力。
当你想得到state
时可以调用 profileStore.getState()
返回 {name: 'Tom', gender: 'male'}
,并且这个state
是经过 Object.freeze
处理,实现了数据不可变功能。
这与redux使用reducer定义数据有很大不同,reducer本质就是把数据的定义和更新糅合在了一起,每次动作都会由它产生返回一个新的state!这种设计有很大的问题,因为它把state的构建交给了用户自己去实现而不是redux自身代替用户去管理!!
需要指出的是reducer吹上天的纯函数,纯函数的存在是为了适应reducer的目的,每次动作都返回一个state,这导致reducer必须是纯函数。
纯函数的存在是为了适应reducer的设计而不是作为一个redux的优点!
所以你会发现,在真实世界中,你总是用一个东西去对接纯函数!troublesome!
现在我们订阅一个副作用:
profileStore.subscribe(state => console.log('subscribe', state))
复制代码
这样,每次update都会执行打印state。
让我们试一下触发更新,触发之前,得拿到profileStore的updater:
const updater = profileStore.getUpdater()
复制代码
现在更新一下名字:
updater.updateName('Jerry')
复制代码
调用后,不仅会更新name
,还会触发一次副作用,打印出 "subscribe {name: 'Jerry', gender: 'male'}"
。
这就是rako最核心的功能。
鼓励创建多个store
rako总是鼓励你将状态平铺,所以提供一个工具 createStores
去创建多个状态容器store
。
const {profile$, contact$, theme$} = createStores({profile, contact, theme})
复制代码
createStores
返回一个对象,对象的key相比传入对象的key会加一个 $
后缀作为 profileStore,contactStore,themeStore
的简写。
createStores传入对象而不是数组的目的是为了可拓展性。至于可拓展性方面,参考redux中间件,但会和redux有所不同,我称之为enhancer,在这篇入门文章不打算深入讲解复杂的东西。 况且,开发enhancer不属于用户关心的事情!你不需要知道任何关于开发enhancer的知识!
高内聚
现在我们聊聊高内聚。 你可以做任何初始化和异步操作在代码内,
function profile(update) {
// 网络请求 fetchProfile,如果网络错误,我未来会提供一个非常优秀的插件去支持错误处理!
fetchProfile().then(({name, gender}) => update({name, gender}))
// 但是你不能同步update
// update({name: 'Jerry'}) wrong!
return {
name: null,
gender: null,
updateName(name) {
// 网络请求 setName
setName(name).then(() => update({name}))
},
updateGender(gender) {
// 网络请求 setGender
setGender(gender).then(() => update({gender}))
}
}
}
复制代码
相比redux将一个简单的逻辑写地又臭又长,rako没有必要写任何样板代码!你可以始终将所有业务逻辑写在一个地方!
如果你真正的理解redux,你就会发现 dispatch、action.type、action.payload
其实和一组概念是一一对应的:
redux | concepts |
---|---|
dispatch | 调用函数 |
action.type | 函数名 |
action.payload | 函数的参数 |
dispatch({type: 'SET_NAME', payload: 'Jerry'})
复制代码
完全等价于
setName('Jerry')
复制代码
说白了,redux是把一个函数的调用复杂化!
不仅于此,由于action.type是一个字符串,所以不论action.type是否正确都会触发一次更新,究其根本是一个不可靠的函数调用,你必须人力去维护一组action.type,而且要保证你的每一次dispatch都要正确执行正确的字符串action.type。
rako并不采用如此设计,如果你想更新,直接调用updateName即可,你想调用一个不存在的更新是不可能的,一句话总结就是redux提供人工级维护,rako提供语言级维护。
在react中使用
好啦!介绍一下rako如何在react中使用!我将rako-react设计得如此简单以至于只有两个API,甚至在大部分时候只需要一个API就能轻松将store注入进component中!
启用decorator:
import {assign, prop} from 'rako-react'
@assign(
prop(profile$, state => state),
contact$
)
class App extends React.Component {}
复制代码
没启用decorator:
export default assign(
prop(profile$, state => state),
contact$
)(App)
复制代码
prop
第一个参数是store
,第二个参数是mapper
,mapper
是一个函数,会将store的state和updater传入其中,返回一个新的object,默认是:
(state, updater) => Object.assign({}, state, updater)
复制代码
assign
接受任意prop
或者store
甚至object
,它会将多个状态平铺开来,传入App。在function component中,你始终不需要prop,你可以这样写:
function App({name, gender, phone}) {
// TODO
}
App = assign(profile$, contact$)(App)
复制代码
未来,等到react 16.7正式推出hook,rako将支持useProp
写法!
const {name, gender, updateName, updateGender} = useProp(profile$)
复制代码
如果你认为rako足够优秀,为了让更多的人看到,也为你以后有机会在项目中使用rako,请一定要点个赞,在github点一个star!!!
Welcome more creative ideas!