美文网首页
Dva最不佳实践

Dva最不佳实践

作者: mah93 | 来源:发表于2019-10-09 16:27 被阅读0次

前言

由于之前写了几个dva的项目,近期没怎么用有些遗忘了,写个小结记录一下。

dva是基于react、react-router、redux封装的一个轻框架。详细的介绍在 dva官网,这里仅仅摘录部分。项目托管在GitHub上,点击这里

特性

  • 易学易用:仅有 6 个 api,对 redux 用户尤其友好
  • elm 概念:通过 reducers, effectssubscriptions 组织 model
  • 支持 mobile 和 react-native:跨平台 (react-native 例子)
  • 支持 HMR:目前基于 babel-plugin-dva-hmr 支持 components、routes 和 models 的 HMR
  • 动态加载 Model 和路由:按需加载加快访问速度 (例子)
  • 插件机制:比如 dva-loading 可以自动处理 loading 状态,不用一遍遍地写 showLoading 和 hideLoading
  • 完善的语法分析库 dva-astdva-cli 基于此实现了智能创建 model, router 等
  • 支持 TypeScript:通过 d.ts (例子)

准备工作

  • 确保 node 版本是 6.5 +
  • cnpmyarn 能节约你安装依赖的时间

Step1. 安装 dva-cli 并创建应用

先安装 dva-cli,并确保版本是 0.7.x。

$ npm i dva-cli@0.7 -g
$ dva -v
0.7.0

然后创建应用:

$ dva new dva-learning

创建成功后进入该文件夹:

$ cd dva-learning 
`项目的目录结构`
dva_learning
|-----mock                                  # => 前端模拟数据
|-----node_modules                          # => 项目依赖采用npm管理,所有包均在此目录。
|-----public                                # => 存放index.html
|-----src                                   
       |------assets                        # => 项目静态资源文件夹(图片等)
       |------components                    # => 无状态组件文件夹
       |------models                        # => 状态model文件夹
       |------routes                        # => 路由配置文件夹,页面存放在该文件夹下
       |------services                      # => 服务层文件夹
       |------utils                         # => 工具函数文件夹
       |------index.css                     # => 全局通用样式
       |------index.js                      # => 单页引用入口js,dva项目初始化
       |------router.js                     # => 全局路由状态管理文件
|-----package.json                          # => npm包管理文件
|-----.eslintrc                             # => 代码规范配置文件
|-----.roadhogrc                            # => 打包配置文件
|-----.roadhogrc.mock.js                    # => 模拟数据配置文件
|-----README.md
`项目的目录结构`
`运行项目`
$ npm start

如果运行成功的话,浏览器会自动弹出并访问8000端口,看到如下画面:

run_success@2x.png

Step2. 配置 antdbabel-plugin-import

antd是由蚂蚁金服开发的一套UI组件,具有学习成本低、上手速度快、实现效果好的特点。十分适合初学者并且与dva无缝接入。如需了解更多请查看 ANT DESIGN

babel-plugin-import 用于按需引入 antd 的 JavaScript 和 CSS,这样打包出来的文件不至于太大。

$ npm i antd --save
$ npm i babel-plugin-import --save-dev

修改 .roadhogrc,在 "extraBabelPlugins" 里加上:

["import", { "libraryName": "antd", "style": "css" }]

Step3. 添加新页面

我们的目标是写一个登录的界面,成功之后显示dva默认的首页。所以在src/routes文件夹下,新建Login.js和Login.css文件。js文件用来写组件布局,css文件用来写样式,默认为js文件和css文件一一对应。

在Login.js中:

`Login.js`
import React from 'react';
import { connect } from 'dva';
import { Input, Icon, Button } from 'antd';
import styles from './Login.css';

function Login() {
  return (
    <div className={styles.inputDiv}>
      <div>
        <Input
          placeholder="用户名"
          prefix={<Icon type="user" />}
          size="large"
          className={styles.inputUser}
        />
      </div>
      <div>
        <Input
          placeholder="密码"
          prefix={<Icon type="lock" />}
          size="large"
          className={styles.inputPass}
        />
      </div>
      <Button type="primary" className={styles.button}>
          登录
      </Button>
    </div>
  );
}

Login.propTypes = {
};

export default connect()(Login);

为这个页面添加样式:

`Login.css`
.inputDiv {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 200px;
  height: 220px;
  margin: -110px 0 0 -100px;
}
.inputUser {
  width: 200px;
}
.inputPass {
  width: 200px;
  margin-top: 20px;
}
.button {
  width: 200px;
  margin-top: 20px;
}

然后修改router.js页面,将新写的登录页面,放到默认显示页面

`router.js`
import React from 'react';
import { Router, Route } from 'dva/router';
import IndexPage from './routes/IndexPage';
import Login from './routes/Login';

function RouterConfig({ history }) {
  return (
    <Router history={history}>
      <Route path="/" component={Login} />
      <Route path="/home" component={IndexPage} />
    </Router>
  );
}

export default RouterConfig;

然后运行,会看到你完成的页面

dva登录@2x.png

Step4. 添加事件

之前完成的页面还没有添加点击事件,接下来添加几行代码,让它可以编辑,可以输入和点击

页面中使用的Input, Icon, Button均是Antd中基本的组件,在 ANT DESIGN有对它们详细的介绍。

为Button添加一个单击事件

`点击button时触发`
const submit = () => {
  alert('here')
};
`在Button中添加这个方法`
<Button type="primary" className={styles.button} onClick={submit}>
登录
</Button>

再次运行项目,点击登录按钮会显示

dva单击事件@2x.png

Step.5 处理逻辑

如果需要这个登录界面更加真实的话,需要处理一些登录中的逻辑,比如点击登录按钮的时候,判断输入框中是否输入了数据等。

dva中有专门的文件夹存放这些处理页面内逻辑的代码。查看src/models/example.js文件,这是一个标准的模版,每个处理逻辑的文件都包含下面几部分

export default {

  namespace: 'example',                                 # => 唯一标识,应用中唯一

  state: {},                                            # => 需要存储的值,每次修改会刷新界面

  subscriptions: {                                      # => 订阅
    setup({ dispatch, history }) {  
    },
  },

  effects: {                                            # => 副作用,一般用来发起请求
    *fetch({ payload }, { call, put }) {  
      yield put({ type: 'save' });
    },
  },

  reducers: {                                           # => 只有在这里才能修改state的值
    save(state, action) {
      return { ...state, ...action.payload };
    },
  },

};

更多关于dva的api,请查看dva APIs

在src/models中新建login.js文件

`login.js`
export default {

  namespace: 'login',

  state: {
    loading: false,
  },

  subscriptions: {
  },

  effects: {
  },

  reducers: {
    save(state, action) {
      return { ...state, ...action.payload };
    }
  },
};

然后在Login.js中调用save方法

`Login.js`
const userName = (e) => {
  dispatch({
    type: 'login/save',
    payload: {
      user: e.target.value,
    },
  });
};

这个方法的意思是:当输入用户名的时候,调用save方法,并将输入的值,保存在state.user中。

Step.6 发送请求

输入完用户名以及密码之后,单击登录按钮,将输入的值发送至后台校验,校验通过之后跳转到下一个页面。

现在已经有了输入的用户名和密码,分别是login.user和login.password,现在需要将这两个数据发送到后台。由于现在并没有后台服务支持,dva支持mock数据,所以先在前台模拟一个后台服务。

在项目根目录下新建.roadhogrc.mock.js并添加:

`.roadhogrc.mock.js`
const mock = {}
require('fs').readdirSync(require('path').join(__dirname + '/mock')).forEach(function(file) {
  Object.assign(mock, require('./mock/' + file))
})
module.exports = mock

之后在mock文件夹中新建login.js并添加:

`login.js`
const qs = require('qs');

module.exports = {
  'POST /login' (req, res) {
    console.log(req.body);
    console.log('接受到请求');
    setTimeout(()=>res.json({code:'200',message:'从mock/example.js请求成功'}),2000)
  },
};

这个的意思是说,监听本地的8000端口,当访问http://localhost:8000/login的时候,会延迟2秒并返回数据。

模拟的后台服务已经完成,现在要在button中添加点击事件,去请求这个接口。

首先改造一下fetch请求,在utils/request.js中:

export default function request(url, method, params) {
  if (method === 'POST') {
    const formData = new FormData();

    for (const key in params) {
      if (params[key] != null) {
        const value = params[key];
        formData.append(key, value);
      }
    }
    return fetch(url, {
      method: method,
      body: formData,
    }).then(checkStatus)
      .then(parseJSON)
      .then(data => ({ data }))
      .catch(err => ({ err }));
  }
  if (method === 'GET') {
    return fetch(url)
      .then(checkStatus)
      .then(parseJSON)
      .then(data => ({ data }))
      .catch(err => ({ err }));
  }
}

然后在services中新建login.js,这里可以理解为转发,从点击事件中传递到request里请求:

import request from '../utils/request';

export function login(params) {
  console.log('services处理');
  return request('/login', 'POST', params);
}

之后在models/login.js,发起这个请求:

  effects: {
    *fetch({ payload }, { call, put }) {  // eslint-disable-line
      if(!payload.userName || !payload.passWord) {
        message.error('请输入账号密码');
        return;
      }
      const { data } = yield call(service.login, payload);
    },
  },

最后在界面的button的点击事件中,调用models里的事件:

  const submit = () => {
    dispatch({
      type: 'login/fetch',
      payload: {
        userName: login.user,
        passWord: login.password,
      },
    });
  };

所有的都完成之后,重启项目,点击button之后可以在命令行中看到:

dva的mock@2x.png

undefined的原因是,roadhog的版本问题,获取不到从前台传递过来的参数。

Step.7 完善细节

整体的流程已经完成,现在要为它添加一些细节,让它看起来更加的真实

添加一个加载等待的圈圈,在点击button的同时显示,后台反馈结果后消失并跳转到下一个界面。

<Spin spinning={login.loading}>
...
</Spin>

用Spin标签将其他的标签包起来,当它显示的时候,会出现在被包裹的标签之上。

通过控制login.loading来控制Spin的显示与消失。

  reducers: {
    ...
    loadingShow(state) {
      return { ...state, loading: true};
    },
    loadingHide(state) {
      return { ...state, loading: false};
    }
  },

最后在请求的时候完成这个流程

  effects: {
    *fetch({ payload }, { call, put }) { 
      if(!payload.userName || !payload.passWord) {
        message.error('请输入账号密码');
        return;
      }
      yield put({ type: 'loadingShow' });
      const { data } = yield call(service.login, payload);
      if(data.code === '200') {
        yield put({ type: 'loadingHide' });
        browserHistory.push('/home');
      }else {
        yield put({ type: 'loadingHide' });
        message.error('登录失败');
      }
    },
  },

如果需要在显示登录页面之前执行某些操作,可以在subscriptions中订阅:

  subscriptions: {
    setup({ dispatch, history }) {
      history.listen((location) => {
        if (location.pathname === '/') {
          message.info('进入了登录页');
        }
      });
    },
  },

总结

这个登录页面,展示了从页面到请求的整个过程,虽然看起来有点绕,涉及了很多的页面。但�是当文件多了,就会体现出dva这样分层的好处:各个文件夹各司其职,功能单一。

最后实现的效果:

dva-learning.gif

相关文章

网友评论

      本文标题:Dva最不佳实践

      本文链接:https://www.haomeiwen.com/subject/lxegpctx.html