(不要忘了點贊轉發關注哦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!