以前寫過一篇 Regular 組件開發的一些建議 的文章提到了平常開發Regular組件的一些槽點,並提出了在簡單需求,不使用狀態管理框架時的一些替代方案。本文的目的即是填前文的一個坑,即較複雜需求下 Redux 引入方案。
html
Redux 是 JavaScript 狀態容器,提供可預測化的狀態管理。前端
const store = redux.createStore(function(prevState, action) {
if (!prevState) {
prevState = {
count: 0
};
}
switch(action.type) {
case 'REQUEST':
return {
count: prevState.count + 1
}
}
return prevState;
});
store.dispatch({
type: 'REQUEST'
});
store.subscribe(function () {
console.log(store.getState());
});
複製代碼
理解他,咱們能夠結合後端 MVC 的 web 模型git
const store = redux.createStore(f);
複製代碼
createStore 這個 API 會建立一臺應用服務器,包含數據庫存儲,以及一個 web Servergithub
const state = store.getState()
複製代碼
getState 操做會返回當前的服務器數據庫數據,這臺數據庫 bind 了 0.0.0.0(閉包),因此外界沒法操做關於數據庫的信息,只能經過內部的服務對數據庫作修改web
store.dispatch({
type: 'type',
payload: {}
})
複製代碼
dispatch(action) 的操做就像是往服務器發送請求。action.type 就像是請求的 uri,action.payload 就像是請求的 body/query。數據庫
const reducer = function (prevState, action) {
if (!prevState) {
return {}
}
switch(action.type) {
// 分發處理
}
return prevState;
}
redux.createStore(reducer);
複製代碼
相應請求會進入對應的控制器(就像 reducer 的 switch 判斷),而控制器內也會對請求攜帶的信息(payload)作分析,來實現對數據庫的增刪改查。redux
整個應用的 state 被儲存在一棵 object tree 中,而且這個 object tree 只存在於惟一一個 store 中。小程序
通常狀況下 createStore 建立的 store ,將做用於整個應用後端
惟一改變 state 的方法就是觸發 action,action 是一個用於描述已發生事件的普通對象。微信小程序
避免人爲的操做 state 變量,形成狀態變化不可預測的狀況,人爲修改 state 的方式被切斷了。
至於如何實現,點我看源碼
createStore
的操做,會建立一個內部變量 state, 因爲 return 出來的 dispatch
和 subscribe
方法保持了對 state 變量的引用,因此 state 會以閉包的形式存活下來。
爲了描述 action 如何改變 state tree ,你須要編寫 reducers。
使用純函數,可測試性和可維護性獲得了保障。
Redux 職責是狀態管理,並不是只限定於前端開發,要使用到 web 開發當中,還缺乏一個部分,完成 MVC 中剩下的一些操做(渲染頁面)。
const store = redux.createStore(reducer);
const ComponentA = Regular.extend({
config() {
const ctx = this;
store.subscribe(function () {
const state = store.getState();
const mapData = {
clicked: state.clicked,
}
Object.assign(ctx.data, mapData);
ctx.$update();
});
}
});
複製代碼
<StoreProvier store={store}>
<Component></Component>
</StoreProvier>
複製代碼
const Component = connect({
mapState(state) {
return {
clicked: state.clicked,
}
}
})(Regular.extend({
config() {
}
}))
複製代碼
Regular.extend({
name: 'StoreProvider',
template: '{#include this.$body}',
config({store} = this.data) {
if (!store) {
throw new Error('Provider expected data.store to be store instance created by redux.createStore()')
}
store.subscribe(() => {
this.$update();
});
}
})
複製代碼
統一從最外層的 StoreProvider 組件獲取 store,保證單一數據源
function getStore(ctx) {
let parent = ctx.$parent;
while(true) {
if (!parent) {
throw new Error('Expected root Component be Provider!')
}
if (parent.data.store) {
return parent.data.store;
}
parent = parent.$parent;
}
}
function connect({
mapState = () => ({}),
dispatch
} = {}) {
return (Component) => Component.implement({
events: {
$config(data = this.data) {
const store = getStore(this);
const mapStateFn = () => {
const state = store.getState();
const mappedData = mapState.call(this, state);
mappedData && Object.assign(this.data, mappedData);
}
mapStateFn();
const unSubscribe = store.subscribe(mapStateFn);
if (dispatch) {
this.$dispatch = store.dispatch;
}
this.$on('destroy', unSubscribe);
}
}
});
}
複製代碼
至此回過頭看 regualr-redux 的架構圖,發現正是 StoreProvier
和 connect
操做幫助 redux
完成了與 MVC 相差的更新視圖操做。
藉助 redux 與特定框架的鏈接器,咱們會發現,對特定 MVVM 框架的要求會變得很低 -- mapState 操做能夠完成 相似 Vue 中 computed/filter 的操做。
因此,現在還在半殘廢中的微信小程序也很適合基於這套思路來結合 redux (逃)。
帶來的好處是,你能夠安心維護你的模型層,不用擔憂 data
過大致使的髒值檢查緩慢,也不須要考慮一些邏輯相關的數據不放入 data
那該放入何處。
相信閱讀此文,你會對 Redux
解決問題的方式有了必定的認識,而繼續深刻的一些方向有:
* middleway 實現原理,redux-logger 和 redux-thunk 等實現方案;
* State 範式化;
* Presentational and Container Components
* 單頁的 redux 架構設計
全文完 ;)
by 君羽
PS:戳我查看原文