redux狀態管理框架zoro簡介
本人是一個dva框架的長期使用者,個人非常喜歡dva對於redux的封裝,但是在使用dva的過程中遇到了許多不是很順手的問題,也因此產生了自己動手編寫一套類dva庫,並致力於解決dva使用過程中的所表現出來的不足,稍後我會介紹在使用過程中遇到的問題,並闡述zoro中如何解決該問題。
本文章適合有一定redux基礎或者使用過dva的讀者
簡述
- 極簡的api,可快速上手
- 簡化redux的應用難度
- 快速接入微信原生小程式,wepy小程式框架,taro小程式框架中,原則上能使用redux的地方皆可以接入
- 內建外掛,支援自定義編寫外掛
- 全域性錯誤處理方案
以下列出相關資料連結:
- ofollow,noindex">zoro github倉庫地址
- zoro-plugin 相關外掛github倉庫地址
- redux redux倉庫地址
- dva dva庫連結
zoro名字的由來
zoro全名roronoa zoro(羅羅諾亞·索隆),是動漫onepiece中的一個劍客,他自身沒有惡魔果實的,確不斷的朝著世界第一大劍豪的目標前進,最終也證明了他在不斷的成長
為什麼編寫zoro庫
我主要列舉一下本人在使用dva過程中遇到過的不足,純屬個人觀點,如有錯誤,還望指出
1. dva在外部呼叫model時dispatch需要給上type型別,不夠直觀
首先我們來看一下dva給出的列子:
import React from 'react' import { connect } from 'dva' import ProductList from '../components/ProductList' const Products = ({ dispatch, products }) => { function handleDelete(id) { // dva中呼叫model action or effect的辦法 dispatch({ type: 'products/delete', // 需指定model namespace payload: id, }) } return ( <div> <h2>List of Products</h2> <ProductList onDelete={handleDelete} products={products} /> </div> ) } export default connect(({ products }) => ({ products, }))(Products)
而我希望在zoro中實現的使用方式如下:
import React from 'react' import { connect } from 'react-redux' import { dispatcher } from '@opcjs/zoro' import ProductList from '../components/ProductList' const Products = ({ dispatch, products }) => { function handleDelete(id) { // 區別主要看這裡 dispatcher.products.delete(id) } return ( <div> <h2>List of Products</h2> <ProductList onDelete={handleDelete} products={products} /> </div> ); }; export default connect(({ products }) => ({ products, }))(Products);
2. dva引入了saga庫作為基礎,增加了入門門檻,(雖然也沒有多少難度)
dva中model定義方式
export default { namespace: 'products', state: [], effects: { *delete({ payload: { id } }, { call, put }) { yield call(deleteProductFromServer, { id }) yield put({ type: 'delete', payload: { id } }) }, } reducers: { delete(state, { payload: id }) { return state.filter(item => item.id !== id) }, }, };
zoro中引入es7語法中的async, await代替saga
export default { namespace: 'product', state: [], effects: { async delete({ payload: { id } }, { put }) { await deleteProductFromServer({ id }) put({ type: 'delete', payload: { id } }) }, }, reducers: { delete({ payload: id }, state) { return state.filter(item => item.id !== id) }, }, }
3. dva中的put中的同步執行在dva1中無法實現,在dva2中作者提供了相應解決辦法,但依舊麻煩
這裡是dva2中的實現方式,通過take等待上一個put執行完畢,再繼續執行,
yield put({ type: 'addDelay', payload: { amount: 2 } }) yield take('addDelay/@@end') const count = yield select(state => state.count) yield put({ type: 'addDelay', payload: { amount: count, delay: 0 } })
zoro中採用async,await,天生所有的effect方法都是一個promise物件,因此也支援put的同步執行
await put({ type: 'addDelay', payload: { amount: 2 } }) const count = select().count await put({ type: 'addDelay', payload: { amount: count, delay: 0 } })
如何在react應用中快速接入zoro
如果你是一個dva使用者,或者你是個前端大牛,下面的介紹可以忽略,直接去倉庫檢視api文件,那裡有更全面的api介紹
接入程式碼如下
import React from 'react' import { render } from 'react-dom' import { Provider } from 'react-redux' import zoro from '@opcjs/zoro' import testModel from './models/test' import App from './components/App' const app = zoro() app.model(testModel) // 註冊單個model // app.model([model1, model2]) // 註冊多個model const store = app.start() // 啟動並建立store render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') )
從程式碼中可以很直觀的發現,接入zoro到react應用只需簡單的4個步驟即可
import zoro from '@opcjs/zoro' const app = zoro()
引入zoro庫,並建立app
import testModel from './models/test' app.model(testModel)
為app引入model,這裡先忽略model改如何定義,後續會著重介紹
const store = app.start()
生成redux store物件用於注入到介面層中
render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') )
將生成的store,通過react-redux Provider 注入到介面元件中
何為model,以及我們該如何定義我們的model
model是用於儲存介面資料的,同時也用於管理資料,包括增刪改查,非同步獲取資料等,下面我將會以todos應用來闡述如何定義我們的model
最簡單的model應用
首先我們需要實現的功能如下,顯示todo列表,刪除
明白了需求,首先我們定義todos的model
export default { namespace: 'todos', // 給model取一個名稱,必須是唯一的不變的 state: [], // model初始值,可以是任意型別 reducers: { add({ payload: { text } }, state) { return state.concat([text]) }, }, }
註冊我們的model到app中
import todos from '../models/todos' app.model(todos) ...省略其他步驟,詳見上一節
接下來我們需要完善我們Todos元件
import React from 'react' import { connect } from 'react-redux' // 引入dispatcher import { dispatcher } from '@opcjs/zoro' const Todos = ({ todos }) => { // 觸發一個todos的新增事件 const handleAdd = () => dispatcher.todos.add({ text: '這是一個新增的代辦事件' }) return ( <div> <ul> {todos.map(todo => ( <li key={todo}>todo</li> ))} </ul> <button onClick={handleAdd}>新增</button> </div> ) } // 通過connect連結state資料到元件中 export default connect(({ todos }) => ({ todos, }))(Todos)
只需最後一步我們便完成了這個需求,連結Todos元件到App元件中
import React from 'react' import Todos from '../components/Todos' const App = () => <Todos /> export default App
到這裡一個最簡單的todos需求便完成了,我們對model的基本定義也有了大體瞭解,假如我們需要在應用初始時獲取伺服器上的todo列表展示呢,該如何完成呢
如何通過model與伺服器實現互動
首先我們為model新增獲取todos的服務
export default { namespace: 'todos', // 給model取一個名稱,必須是唯一的不變的 state: [], // model初始值,可以是任意型別 reducers: { add({ payload: { text } }, state) { return state.concat([text]) }, // 新增update reducers update({ payload }) { return payload }, }, effects: { // 新增queryTodos從伺服器中獲取列表資料 async queryTodos({ payload }, { put, select }) { // 省略getTodosFromServer,這是一個ajax請求,如axios, fetch等 //與伺服器通訊,返回列表,該函式必須返回Promise物件 const { todos } = await getTodosFromServer(payload) // 通過select獲取當前的todos列表,select用法詳見api文件 const currentTodos = select() // put功能很多,可以發起本model的reducer,effect // 也可以發起外部model, 但需指定namespace // 如put({ type: 'test/update' }) 發起test model中的某個方法 // 如果put發起的是一個非同步的effect,必要時可以通過await等待結果 // 更多用法詳見api文件 put({ type: 'update', payload: currentTodos.concat(todos) }) }, }, }
定義好了model,接下來我們我們只需在元件掛載時呼叫該方法即可
import React from 'react' import { connect } from 'react-redux' // 引入dispatcher import { dispatcher } from '@opcjs/zoro' class Todos extend React.Component { componentDidMount() { // 初始化掛載時呼叫獲取非同步資料 dispachter.todos.queryTodos() } handleAdd() { dispatcher.todos.add({ text: '這是一個新增的代辦事件' }) } render () { return ( <div> <ul> {this.props.todos.map(todo => ( <li key={todo}>todo</li> ))} </ul> <button onClick={handleAdd}>新增</button> </div> ) } } // 通過connect連結state資料到元件中 export default connect(({ todos }) => ({ todos, }))(Todos)
到這裡一個簡單的zoro接入教程就結束了,其中還有很多好玩有用的特性沒能介紹,比如外掛機制,全域性錯誤捕獲等,這是我第一個寫教程,不知調理是否清晰易懂,望各位看官輕噴,有問題請指正,讓我們共同進步,不斷的造出更多更簡易的輪子
目前zoro v2版本已經應用與實際小程式專案中

xmpg.jpeg
安裝方法和更多使用教程詳見github zoro
最後還是那句話,開源不易,如果好用,請給個star,謝謝:pray: