关于Dva:
dva 是一个基于 redux 和 redux-saga 的数据流方案,然后为了简化开发体验,dva 还额外内置了 react-router 和 fetch,所以也可以理解为一个轻量级的应用框架,或者可以理解为react框架插件处理的全家桶.
使用Dva:
Dva的使用可以前往Dva的官方网站: https://dvajs.com/guide/getting-started.html
若是需要快速创建一个React 集成Dva的项目,可以使用dva-cli脚手架工具:
npm install dva-cli -g
dva new dva-quickstart
cd dva-quickstart
npm start
驱动开发: 搭建简易Dva测试用例:
import React, { Fragment } from "react"
import dva, { connect } from "./dva"
import { Router, Route, routerRedux} from "./dva/router"
// 定义一个异步处理函数, 用于之后处理dva中的异步处理逻辑
const delay = (ms) => new Promise(function(resolve, reject){
setTimeout(function(){
resolve()
}, ms)
})
// 1. 通过指定dva方法获取一个app的应用实例原型
const app = dva()
// 2. 注册模型,每个模型都有自己的命名空间
// 同时还可以定义初始状态和处理函数reducers, 当每次请求到了都会执行reducers中的函数
// 可以定义多个model, 多个model会进行合并,合并之后通过namespace进行区分
app.model({
namespace: "counter",
state: {number: 0},
reducers: {
add(state, action){
return {number: state.number + 1}
}
},
// 在effects中放的都是generator生成器
effects: {
// 这里的异步处理会依托于saga库实现,所以这里的put和call方法都会是之后saga提供的, 他会生成一些执行对象
// 这里的call用于调用方法, put用于派发动作
*asyncAdd(action, {put, call}){
// yield调用一个delay方法,返回一个promise, 此时会等待在这里,直到Promise变成完成态
yield call(delay, 1000);
// 在延迟执行结束后派发一个动作
yield put({type: 'counter/add'})
}
}
})
// 准备要渲染的组件, 通过dav中的connect将model个Component进行关联
const Counter = connect(state => state.counter)(props => ((
<div>
<p>{props.number}</p>
<button onClick={() => props.dispatch({type: "counter/add"})}>add</button>
{/* 执行异步逻辑 */}
<button onClick={() => props.dispatch({type: "counter/asyncAdd"})}>asyncAdd</button>
</div>)))
const Home = () => <div>Home</div>
// 配置 dva 路由
app.router(({history, app}) => {
return <Router history={history}>
<Fragment>
<Route path="/" exact component={Home} />
<Route path="/counter" exact component={Counter} />
</Fragment>
</Router>
})
// 启动渲染, 类似于: ReactDOM.render(() => <App />, document.querySelector("#root"))
app.start("#root")
根据以上的用例实现简易Dva:
- 根据以上的用例分析,我们需要实现dva的redux状态库功能,saga的异步处理功能,由于导入的路由时直接来源于react-router-dom库的,所以不需要处理.
/**
* @description 搭建简易版dva库
* @author 一树梨花,静开静落
*/
import { createStore, combineReducers, applyMiddleware } from "redux"
import React from "react"
import ReactDOM from "react-dom"
import { Provider, connect } from "react-redux"
import createSagaMiddleware from "redux-saga"
import * as effects from "redux-saga/effects"
import { createHashHistory } from "history"
const NAMESPACE_SEPERATOR = '/'
// 这里直接导出react-redux的connect方法用于组件使用store
export {
connect
}
// 1. dva首先是一个函数,执行dva函数获取app实例
export default function(){
// 创建dva导出的app对象
let app = {
model,
_models: [],
router,
_router: null,
start
}
function model(model){
// 调用model方法时直接将model存放起来
app._models.push(model)
}
function router(routerConfig){
// 调用router方法时直接将路由方法保存起来, 再start得时候执行
app._router = routerConfig
}
// 启动应用进行渲染
function start(root){
// 将model中得数据进行合并为reducers
let reducers = {};
for(let model of app._models){
// 取出models中的model进行处理成combineReducers方法接收处理的reducer形式
/**
* combineReducers({
* namespace: function(state, action){} 这里的方法就是redux中的reducer的方法
* })
*/
reducers[model.namespace] = function (state = model.state, action){
let actionType = action.type; // 获取动作类型
let values = actionType.split(NAMESPACE_SEPERATOR); // 从action中提取出相应得处理方法信息
if(values[0] === model.namespace){
// 检测当前的命名空间是否匹配, 匹配后获取当前要进行计算状态的reducer
let reducer = model.reducers[values[1]];
if(reducer){
return reducer(state, action)
}
}
return state;
}
}
let reducer = combineReducers(reducers); // 合并reducer
// 利用sage处理异步执行逻辑
let sagaMiddleware = createSagaMiddleware()
function* rootSaga(){ // 处理异步逻辑, 将effects中的异步执行放到新的一个进程中执行, 这是saga这个库处理的
for(const model of app._models){
for(const key in model.effects ){
// 启动一个进程去处理异步动作
yield effects.takeEvery(`${model.namespace}${NAMESPACE_SEPERATOR}${key}`, model.effects[key], effects)
}
}
}
// 1. 创建store仓库, 基于redux实现:
let store = createStore(reducer, applyMiddleware(sagaMiddleware))
// 启动saga中间件:
sagaMiddleware.run(rootSaga)
// 2. 调用_router方法获取应用得router路由
const history = createHashHistory()
let App = app._router({app, history});
// 3. 使用ReactDOM进行挂载,同时使用react-redux对store仓库进行处理
ReactDOM.render(<Provider store={store}>
{App}
</Provider>, document.querySelector(root))
}
return app;
}
- 关于为什么要使用dva库, 说说我的看法: dva库集成了大部分react开发中经常会使用到的一些库方法,通过dva进行一层浅封装,使得我们可以不用去安装很多的第三方库,统一从dva中进行导出使用,dva进行统一管理,开发体验会更好。当然,对于其中使用redux状态管理库和使用saga的异步逻辑处理中间件的优势好处就更不必多说了。 关于手写 dvajs 简易版就说到这里了,大家加油 ! !