• 使用umi+dva做一个demo


    最初只是使用react 进行开发项目,发现项目过大状态管理起来就相当困难,虽然有redux, mobx,但是使用起来还是相当繁琐,而目前umi有现成的轮子使用简单,当然愿意尝试了,趁现在假期有时间简单学习记录一下

    一、安装umi

    还是原来的套路,要使用先是安装一伙

    $ npx @umijs/create-umi-app // if use npm
    

    好了命令执行完成后目录是这样的
    app directory
    好接下来该开始着手开发了,但还有一件要注意的事情—

    Umi官网目前已经是3.x版本了,注意这个版本与2.x有相当大差距,如:plugins 的配置

    export default {
    - plugins: [
    -   ['umi-plugin-react', {
    -     dva: {},
    -     antd: {},
    -     ...
    -   }]
    - ],
    + dva: {},
    + antd: {},
    + ...
    }
    

    其他还有很多,如果刚从2.x升级3.x就要多留意了,否则会浪费不必要的时间,具体参照upgrade-to-umi-3

    二、首页

    使用antd layout 创建一个首页,这个demo比较简单,所有就直接官网照搬了,部分片段如下

    import { Layout, Menu, Badge, Dropdown } from 'antd';
    const { Header, Footer, Content } = Layout;
    const logo = require('../../public/course/logo.jpeg');
    
    export default function Page(props) {
    	return (
    	    <Layout>
    	      <Header className={styles.header}>
    	        <img className={styles.logo} src={logo} alt="" />
    	        <Menu
    	          theme="dark"
    	          mode="horizontal"
    	          selectedKeys={selectedKeys}
    	          style={{ lineHeight: '64px', float: 'left' }}
    	        >
    	          <Menu.Item key="/">
    	            <Link to="/">商品</Link>
    	          </Menu.Item>
    	          <Menu.Item key="/users">
    	            <Link to="/users">用户</Link>
    	          </Menu.Item>
    	          <Menu.Item key="/about">
    	            <Link to="/about">关于</Link>
    	          </Menu.Item>
    	        </Menu>
    	      </Header>
    	      <Content className={styles.content}>
    	        <div className={styles.box}>{props.children}</div>
    	      </Content>
    	      <Footer className={styles.footer}>learning</Footer>
    	    </Layout>
    	  );
      }
    

    三、路由配置

    一个项目中路由应该是不可缺少的一部分,umi 3.x配置是直接在 .umirc.ts文件中

    import { defineConfig } from 'umi';
    
    export default defineConfig({
      nodeModulesTransform: {
        type: 'none',
      },
      antd: {
        compact: true,
      },
      dva: {
        immer: true,
        hmr: false,
      },
      routes: [
        {
          path: '/',
          component: '@/layouts/index',
        }
      ],
      fastRefresh: {},
    });
    
    

    这里开启了antd 和dva 的插件使用,因为后期可能会使用到,好了这是可以yarn start 运行看看有一个简单的主页面了,但是点击一定会报错(路由没有配置完整)

    四、鉴权

    配置一个页面检测是否登录,当然首要任务是完善上面首页 中对应的菜单路由及页面创建了

    app directory
    创建目录和对应的文件,再完善路由

    import { defineConfig } from 'umi';
    
    export default defineConfig({
      nodeModulesTransform: {
        type: 'none',
      },
      antd: {
        compact: true,
      },
      dva: {
        immer: true,
        hmr: false,
      },
      routes: [
        { path: '/login', component: '@/pages/login' },
        {
          path: '/',
          component: '@/layouts/index',
          routes: [
            { path: '/', component: '@/pages/goods' },
            {
              path: '/about',
              component: '@/pages/about'
            },
            {
              path: '/users',
              component: '@/pages/users/index',
              routes: [
                {
                  path: '/users/:oid/:id',
                  component: '@/pages/users/$oid/$id',
                },
                { component: '@/pages/NotFound' },
              ],
            },
            { component: '@/pages/NotFound' },
          ],
        },
        { component: '@/pages/NotFound' },
      ],
      fastRefresh: {},
    });
    
    

    好了路由大概是这个样子了,基本完成,

    现在我们假如对about 页面做一个鉴权,其实也简单,创建一个wrappers目录,再在下面创建一个auth.tsx, 内容如:

      
    import { Redirect } from 'umi'
    
    export default (props:any) => {
    // 模拟鉴权
      if (Math.random() > 0.5){
        return <div>{ props.children }</div>;
      } else {
        return <Redirect to="/login" />;
      }
    }
    

    再在about 路由增加wrappers, 修改如:

            {
              path: '/about',
              component: '@/pages/about',
              wrappers: ['@/wrappers/auth'],
            },
    

    好了点击的时候就有50%机率到登录页面了

    五、mock

    目前是一个demo没有后台,所有使用mock模仿异步请求,在mock目录下创建一个login.js,简单的写一个login的接口

    export default {
      'post /api/login': (req, res) => {
        const {
          username,
          password
        } = req.body;
        if (username == 'admin' && password == '123') {
          var resObj = res.json({
            code: 0,
            data: {
              token: 'admin123',
              role: 'admin',
              balance: 1000,
              username: 'admin',
            },
          });
          return resObj;
        }
    
        if (username == 'dex' && password == '123') {
          return res.json({
            code: 0,
            data: {
              token: 'dex123',
              role: 'user',
              balance: 100,
              username: 'dex',
            },
          });
        }
        return res.status(401).json({
          code: -1,
          msg: '密码错误',
        });
      },
    };
    
    

    六、models

    目前还需要一个与mock接口进行交付的server,于是在models下创建一个user.js来管理用户登录相关的操作

    import reqService from 'axios';
    import { history } from 'umi';
    
    const userinfo =
      JSON.parse(localStorage.getItem('userinfo')) |
      {
        token: '',
        role: '',
        username: '',
        blance: '',
      };
    // 登录请求
    function loginReq(payload) {
      return reqService.post('/api/login', payload).then((res) => {
        return {
          code: res.data.code,
          userinfo: res.data.data,
        };
      });
    }
    
    export default {
      namespace: 'user',
      state: userinfo,
      effects: {
        *login({ payload }, { call, put }) {
          try {
            const { code, userinfo } = yield call(loginReq, payload);
    
            localStorage.setItem('userinfo', JSON.stringify(userinfo));
            // 触发reducers更新状态
            // 犹如import { call, put, takeEvery } from 'redux-saga/effects'中的takeEvery
            yield put({
              type: 'init',
              payload: userinfo,
            });
            history.push('/');
          } catch (error) {
            console.log(error);
          }
        },
      },
      reducers: {
        init(state, action) {
          // 将状态更新到state
          return action.payload;
        },
      },
    };
    

    这里使用umi 就比较方便,不需要直接去进行redux-saga的操作,直接使用effects和reduces函数进行数据读取状态更新即可,而这里使用到了es6的generate 生成器函数,直接将异步当成同步来进行操作更加方便了

    六、login

    有了mock 和models 现在可以完善一下登录了,pages下创建login目录再在其下创建index.js,内容大概是这样子

    import React, { Component } from 'react';
    import styles from './index.css';
    import ProForm, { ProFormText } from '@ant-design/pro-form';
    import { UnlockOutlined, UserOutlined } from '@ant-design/icons';
    import { connect } from 'dva';
    const logo2 = require('../../../public/course/logo2.jpeg');
    
    @connect()
    class index extends Component {
      onSubmit = (values) => {
        if (values) {
          this.props.dispatch({ type: 'user/login', payload: values });
        }
      };
      render() {
        return (
          <div className={styles.loginForm}>
            <ProForm
              onFinish={(values) => this.onSubmit(values)}
              submitter={{
                searchConfig: {
                  submitText: '登录',
                },
                submitButtonProps: {
                  size: 'large',
                  style: {
                    width: '100%',
                  },
                },
                // 完全自定义整个区域
                render: (_, dom) => dom.pop(),
              }}
            >
              <h1
                style={{
                  textAlign: 'center',
                }}
              >
                <img className={styles.logo} alt="logo" src={logo2} />
              </h1>
              <div
                style={{
                  marginTop: 12,
                  textAlign: 'center',
                  marginBottom: 40,
                }}
              >
                WE USE ANTD DESIGN TO CREATE A LOGIN PAGE.
              </div>
              <ProFormText
                fieldProps={{
                  size: 'large',
                  prefix: <UserOutlined />,
                }}
                name="username"
                placeholder="admin"
                rules={[
                  {
                    required: true,
                    message: '请输入用户名!',
                  },
                ]}
              />
              <ProFormText.Password
                fieldProps={{
                  size: 'large',
                  prefix: <UnlockOutlined />,
                }}
                name="password"
                placeholder="123"
                rules={[
                  {
                    required: true,
                    message: '请输入密码!',
                  },
                ]}
              />
            </ProForm>
          </div>
        );
      }
    }
    
    export default index;
    
    

    其中使用到了dva进行state 引入,使用dispatch 触发user/login 进行登录模拟操作,缓存用户信息,注意connect中有两个参数,第一个是

    state,而state可根据models下面的文件中的namespace 调用不同的操作。第二个参数是一个对象 可以根据namespace映射对应的actions

    @connect(
      (state) => ({
        loading: state.loading,
        tags: state.goods.tags,
        courses: state.goods.courses, // 获取goods 下的courses属性的值
        userinfo: state.user, // 获取user 下对应的userinfo的值
      }),
      {
        getList: () => ({
          type: 'goods/getList',
        }),
        addCart: (payload) => ({ 
          type: 'cart/addCart', // 映射cart文件中的addCart
          payload, // 同时将payload 作为方法参数传入
        }),
      },
    )
    

    七、test

    最后还有个商品列表页面,基本上套路和上面差不多,就不记了,最后测试哈

    好了这个demo学习完毕,源码也提交到github保存下umi-dva-antd-react-demo

  • 相关阅读:
    解决WordPress不能发邮件,WordPress 无法发送邮件
    WordPress 显示 Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 2097160 bytes)解决办法
    怎么优雅的取消重复请求ajax
    手拉手搭建一个脚手架
    数据库隔离级别RC与RR区别——MVCC、ReadView
    整理一下下一步的计划
    减肥
    EBS: Shift + F6: 当复制上行记录
    Oracle 表值函数之多表关联使用
    EBS: 序号授权 GRANT
  • 原文地址:https://www.cnblogs.com/dengxiaoning/p/14983360.html
Copyright © 2020-2023  润新知