基于react+redux的轻量级框架dva初使用及应用逻辑分析

来源:互联网 发布:软件开发做账 编辑:程序博客网 时间:2024/06/10 14:55

随着React的大火,flux架构也跟着火了起来,Redux是实现flux架构的库中的佼佼者,使用react+redux来开发应用的开发者也越来越多

确实,React+Redux+webpack+babel+es6,这是一个极具生产力的组合

源自flux架构的单向流动使得应用的逻辑和数据流动变得可控,当应用逻辑变得复杂的时候,其优势越加明显,开发越加高效

但是在开发的时候出现了一些问题,比如说概念太多,文件太松散等等问题

以及为了解决异步交互问题,会引用一些中间件来处理store,比较常见的是用thunk中间件+asysc或者用saga等等

图为一个中间件处理store的代码

这不是Dva解决的最大的问题,Dva的重点是引进了model这个概念,将以前一堆松散的概念和文件组织到一起了,这才是关键

React+redux+redux-saga中,每新增1个组件或者说新增1个页面,都要新增3个文件,sagas,reducers,action各自对应1个文件,编写的时候不停的切换,极其的麻烦,而且文件多了之后,文件的管理不是很方便

DVA正是解决这些问题的


首先,我想说一个问题,那就是……Dva这个名字……

咳,好吧,最初的最初我关注到这个框架,就是因为它的名字,然后就好奇的点进去了,然后就被它的优雅给吸引,再也不可自拔……

然后它的配置文件,叫roadhog

如果你是一名OWer,那么你已经想迫不及待的看下去了

为什么要用DVA呢?

以下是官方文档的说明:

易学易用:仅有 6 个 api,对 redux 用户尤其友好

elm 概念:通过 reducers, effects 和 subscriptions 组织 model

支持 mobile 和 react-native:跨平台

支持 HMR:目前基于 babel-plugin-dva-hmr 支持 components、routes 和 models 的 HMR

动态加载 Model 和路由:按需加载加快访问速度

插件机制:比如 dva-loading 可以自动处理 loading 状态,不用一遍遍地写 showLoading 和 hideLoading

完善的语法分析库 dva-ast:dva-cli 基于此实现了智能创建 model, router 等

支持 TypeScript:通过 d.ts


Let’s start!

首先奉上Dva官方文档地址,是发布在github上的:dva

首先是安装:

使用npm安装即可: npm install dva-cli@0.7 -g

需要注意的是:1.node版本必须在6.5以上 2.dva-cli必须版本在0.7.x(可以安装完dva-cli之后用dva -v查看一下版本号)

以上两点都是DVA官方的要求

然后使用new命令创建一个新的dva应用: dva new dva-demo

耐心等待创建完成

这是应用创建成功的提示

好的,现在我们来看创建好的工程~

可以先运行一下试试: npm run start

运行结果如图

url为:http://localhost:8000/#/?_k=gebc0m

嗯 一个奇怪的url

不过如果你对前端路由,或者说对react-router有所了解的话,就知道这是hashHistory

如果不习惯可以修改为browserHistory

在根目录下的index.js文件中

修改代码

再运行看看效果

url正常了

不过路由改成browserHistory的话,还需要后端代码的配合


现在我们来观察这个创建好的工程的目录

可以很明显的看到代码都放在src文件夹了

另外在public文件夹下放了一个index.html文件

我们打开它来看看

<!DOCTYPE html><html lang="en"><head>  <meta charset="UTF-8">  <meta name="viewport" content="width=device-width, initial-scale=1">  <title>Dva Demo</title>  <link rel="stylesheet" href="/index.css" /></head><body><div id="root"></div><script src="/index.js"></script></body></html>

很明显,这是一个React典型应用的html模版文件,和用create-react-app脚手架创建的应用中生成的index.html内容差不多

我们几乎不对这个html进行代码改动,也不在里面加入html标签,css文件等,整个文件的重点只是body里的两行代码

<div id="root"></div>

这是确定了id的div标签,我们编写的React组件最后都是展示在这个div块里

<script src="/index.js"></script>

最后所有js文件都会打包到这个Js文件里统一加载

再来看js文件

整个程序的入口文件是根目录下的index.js文件

import dva from 'dva';import { browserHistory } from 'dva/router';import './index.css';// 1. Initializeconst app = dva({  history: browserHistory,});// 2. Plugins// app.use({});// 3. Modelapp.model(require('./models/example'));// 4. Routerapp.router(require('./router'));// 5. Startapp.start('#root');

dva-cli生成的文件给我们写的注释已经写的很清楚了

先初始化,再加载插件,再加载Model,再加载路由,最后启动程序

我们就根据这个过程来理清楚文件关系


先从第4步路由来分析

加载路由的语句为: app.router(require('./router'));

很明显能看出,加载的是同在根目录下的router.js文件

我们打开router.js文件

import React from 'react';import { Router, Route } from 'dva/router';import IndexPage from './routes/IndexPage';function RouterConfig({ history }) {  return (    <Router history={history}>      <Route path="/" component={IndexPage} />    </Router>  );}export default RouterConfig;

可以很明显的看出,主体结构是配置react-router,拦截”/”,然后渲染IndexPage

根据import语句,找到routes目录下面的IndexPage.js

import React from 'react';import { connect } from 'dva';import styles from './IndexPage.css';function IndexPage() {  return (    <div className={styles.normal}>      <h1 className={styles.title}>Yay! Welcome to dva!</h1>      <div className={styles.welcome} />      <ul className={styles.list}>        <li>To get started, edit <code>src/index.js</code> and save to reload.</li>        <li><a href="https://github.com/dvajs/dva-docs/blob/master/v1/en-us/getting-started.md">Getting Started</a></li>      </ul>    </div>  );}IndexPage.propTypes = {};export default connect()(IndexPage);

显然,这是一个React组件,而且是Redux中所说的容器型的组件,因为在导出的时候通过connect这个高阶函数来装饰这个组件,以便能够从Redux状态树中获取数据

因为这是一个很简单的页面,并没有导入在Components目录下封装好的组件,如果是在实际开发,页面足够复杂的时候,会在Component中封装组件,然后在Routes目录下的中的组件导入,然后再导入路由文件中

components目录下的example.js

import React from 'react';const Example = () => {  return (    <div>      Example    </div>  );};Example.propTypes = {};export default Example;

只是1个示例文件,没有使用它,它本身也没什么意义

我们再回到根目录下的index.js中,第4步路由部分加载的文件我们已经回溯到底了,我们来看第3步model

app.model(require('./models/example'));

可以看出,加载的是models目录下的example.js文件

打开查看其代码

export default {  namespace: 'example',  state: {},  subscriptions: {    setup({ dispatch, history }) {  // eslint-disable-line    },  },  effects: {    *fetch({ payload }, { call, put }) {  // eslint-disable-line      yield put({ type: 'save' });    },  },  reducers: {    save(state, action) {      return { ...state, ...action.payload };    },  },};

model这个概念,是dva的核心,可以看到上面代码中主要分为几个字段:namespace,state,subscriptions,effects,reducers

这几个字段,官方文档中给出的说明是这样的:

namespace - 对应 reducer 在 combine 到 rootReducer 时的 key 值

state - 对应 reducer 的 initialState

subscription - elm@0.17 的新概念,在 dom ready 后执行

effects - 对应 saga,并简化了使用

reducers- 还是对应原来的reducer,概念没有变化

熟悉react,redux, redux-saga 这一套应用架构的,无缝切换

可以看到,关于redux中action,reducer的那一堆,都集中在model里面了,编写代码再也不用切换过去切换过来,而且在一个文件中,组织代码非常方便,再也不用code everywhere

services目录下的文件是为models目录下的文件服务的

更多的,可以去看官方给出的一个CURD用户管理的demo

12 步 30 分钟,完成用户管理的 CURD 应用

原创粉丝点击