各類優勢就不說了,有兩點以爲不爽的地方:html
示例工程,文件夾太散。具體而言action
,reduce
,store
,三者的對應關係很固定,分在三個文件夾下,實在太散。react
redux
的源碼使用了大量的函數式
的pattern。本人不是很習慣(好像暴露了-_——!)git
因此,我就擼了一個面向對象風格的簡易版,代碼數少到不行,代碼在github上,(還附帶了react-redux
的connect
的簡易版,不過方面的代碼註釋掉了,由於這是爲了配合react的功能,而redux的設計有獨立於react的初衷。github
在本人的一個demo項目中,使用下來,感受還行。demo地址 樣式上主要適配手機,pc上也能看,另外控制檯有列表頁加載數據的log,以下圖。demo的代碼暫時不開放,須要整理。
express
action
,reduce
必需要使用者提供.同redux
同樣,要求每次reduce
返回全新的state
對象redux
dispatch
的功能相對固定,另外提供一個subscribe
方法,用來註冊監聽器,dispatch
時,通知全部監聽者。api
根據這些要求,寫一個BaseStore。用戶須要繼承BaseStore,提供reduce
函數react-router
export default class BaseStore{ listeners=[] dispatch(action){ this.state = this.reduce(action); this.listeners.forEach(listen=>{ listen(action,this.getState()) }) } subscribe(listener){ var index = this.listeners.length this.listeners.push(listener); return (index)=>{ return ()=>{ this.listeners.splice(index); } }(index) } reduce(action){ throw new Error('subClass fo BaseStore should implement reducer function') } getState(){ return this.state; } }
使用的時候,只需繼承BaseStore
,提供reduce
函數就行app
import Immutable from 'immutable'; import {BaseStore} from 'zlux' const ActionTypes={ ADD:'ADD', DELETE_BY_ID:'DELETE_BY_ID', UPDATE:'UPDATE' } export default class SimpleStore extends BaseStore{ __className ='PostListStore' state = new Immutable.List() reduce(action){ if(action.type === ActionTypes.ADD){ return state.push(action.payLoad) } if(action.type === ActionTypes.DELETE_BY_ID){ let id = action.payLoad.id; return state.filter(item=>{return item.id !==id}) } if(action.type == ActionTypes.UPDATE){ var id = action.payLoad.id; var index = state.findIndex(item=>{return item.id == id}); //if index == -1, 這裏不考慮update時,沒有相應item的狀況 return state.set(index,action.payLoad) } return state; //注意:默認返回原state } //提供方便外接調用的方法 add(payLoad){ this.dispatch({ type:ActionTypes.ADD, payLoad }) } deleteById(payLoad){ this.dispatch({ type:ActionTypes.DELETE_BY_ID, payLoad }) } update(payLoad){ this.dispatch({ type:ActionTypes.UPDATE, payLoad }) } }
而後像這樣使用:dom
var ss = new SimpleStore() ss.add({id:1,content:'hello'}) ss.update({id:1,content:'world'}) ss.deleteById({id:1})
每次調用dispatch,ss.getState()
都會返回最新的state。由於使用了immutable
的list,確保每次的state
都是全新的。
關於redux的中間件的前因後果,官方文檔已經說得不能在詳細,文檔地址
這裏實現中間件,故意作的和express
中的用法很像。以下:
simpleStore.use( function(next,action,store){ console.log('before') next() console.log('after') }, function(next){ if(isLogin){ next() } else{ goToLogin(); } } )
稍微有些區別,
須要用中間件,要一次性的傳個use
函數,屢次使用use
,後面的會覆蓋前面的。
中間件函數中,參數只有next是必須的。action
和store
都是自動注入的。須要用就寫上,不須要用,就不用管。
暫時沒使用error first的模式。我的認爲state
中徹底能夠體現錯誤信息。
中間件實現的核心代碼以下:
use(...fns){ this.middlewareFns = fns; var _this =this; this.wrappedDispatch= this.middlewareFns.reduceRight((a,b)=>{ return ()=>{ b(a,_this.__curAction,_this) } },this.__dispatch)//__dispatch是原始的dispatch實現。 } //改寫上面的dispatch實現。 dispatch(action){ this.__curAction = action this.wrappedDispatch(); }
redux
的實現都是不依賴於react,只要合適,任何環境下都能使用。爲了更好的和react
配合使用,redux官方還提供了 react-redux
.
react-redux
裏的connect
,幫助Redux store
(由Provider
傳入的合併過的store)的中狀態、方法和container
進行綁定。
react-redux
要求整個應用只有一個redux store
,是由多個單純store(使用單純store
來區分redux store
)是合併而成。container
能夠對應多個單純store。
使用者(也就是你)選取Redux store
中的須要的state
,dispatch
, 交由connect
去綁定到react組件的props中。
指定了哪些 Store/State
屬性被映射到 React Component
的 props
,這個過程被稱爲 selector
.
若是很是在乎性能,避免沒必要要的計算,還須要經過reselect
這樣的庫。
而我這裏要求單純的store和container是一對多的關係。若是一個container
須要多個store,那麼經過拆分container,而不是合併store。
這樣就要求container
是最小的頁面構成單位,應該作到原子化。
這樣,container是否須要經過setState()
來render組件,只要比較對應
的單純store
的state,是否是同一個(還記得嗎,任何的狀態改變,都返回全新的state,因此這個判斷很是快)
只因此這樣要求,是由於好實現(找了那麼多借口,最後暴露了-_——!)。固然我好實現,使用者就得多寫點代碼,可是結構上,我我的以爲更清晰點。
不過由於我還沒寫個特別大型的項目,不知道拆分container
而不是合併store,是否是能知足複雜應用的開發。
import {Component} from 'react'; import getStoreShape from './getStoreShape.js' export default (WrappedContainer,store) => { return class extends Component{ state={} constructor(props,context){ super(props,context) this.state.props = store.getState(); this.unsubscribe = store.subscribe(()=>{ //直接判斷state的引用是否變化,shouldComponentUpdate都不須要了 if(this.state.props == store.getState()){return ;} //這裏的props隨便取的名字,沒有意義 //setState只是用於通知從新渲染。 this.setState({ props:store.getState() }) }) } componentWillUnmount(){ this.unsubscribe(); } //context機制,TODO test case static childContextTypes={ store:getStoreShape } getChildContext() { return { store: this.store }; } getWrappedInstance() { return this.refs.wrappedInstance; } render(){ return( <WrappedContainer ref='wrappedInstance' {...this.props} store={store} /> ) } } }
總體是,就是一個 high order component
在constructor
裏,註冊了對store
的監聽。這是一個單純store,直接比較state是否變化,就知道要不要重新render。
使用的話,貼點代碼:
//postListStore 是已經實例化的單純store,能夠被多個container公用。 const PostListElement = enhanceWithStore(PostListContainer,postListStore) const PostDetailElement = enhanceWithStore(PostDetailContainer,postDetailStore) //... //使用react-router React.render( ( <Router> <Route path="/" component={App}> <IndexRoute component={PostListElement} onEnter={()=>{ utils.Scroll.restoreScroll('PostList') }} onLeave={()=>{ utils.Scroll.saveScroll('PostList') }} /> <Route path="posts/:postId" component={PostDetailElement} /> </Route> </Router> ), document.getElementById('mount-dom') )
核心的功能就這些。
真正serious的實際項目,你們仍是用用redux
吧,配套齊全。自擼的項目,本身先踩踩坑~。