剖析Dva原理: 手写Dva

关于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 简易版就说到这里了,大家加油 ! !
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值