react+typescript+umi+dva+antd

4,047 阅读4分钟

react+typescript+umi+dva+antd

背景

  • React
    前端三大框架之一。
  • typescript
    javascript的超集
  • Dva
    由阿里架构师 sorrycc 带领 team 完成的一套前端框架,在作者的 github 里是这么描述它的:”dva 是 react 和 redux 的最佳实践”。
  • Antd
    是阿里的一套开箱即用的中台前端/设计解决方案,UI框架。
  • Umi
    一套可插拔的企业级 react 应用框架,同样由dva作者 sorrycc 完成。他在Umi中引入了 UI 工具 antd,打包工具 roadhog,路由 react-router和状态管理器 dva,做到了可插拔机制。

创建一个umi应用

  1. 先安装node,再创建一个项目文件夹,通过脚手架创建umi项目

    npx @umijs/create-umi-app
    

    在这里插入图片描述
    在这里插入图片描述

  2. 安装依赖

    npm i 
    
  3. 运行项目

    npm run start
    

    在这里插入图片描述

umi应用的路由

配置路由

在配置文件.umirc.ts中通过 routes 进行配置,格式为路由信息的数组。

routes: [
  { path: '/', component: '@/pages/Index' },
  { path: '/User', component: '@/pages/User' },
],

在这里插入图片描述

路由配置工程化

为了让代码更工程化,我们可以将路由配置单独拆分成一个文件
router.tsx

const router: any = [
  { path: '/', component: '@/pages/Index' },
  { path: '/User', component: '@/pages/User' },
]

export default router;

.umirc.ts

import { defineConfig } from 'umi';
import router from '@/router/router'; // 引入路由配置文件

export default defineConfig({
  nodeModulesTransform: {
    type: 'none',
  },
  routes: router,
});

在这里插入图片描述

约定式路由

除配置式路由外,Umi 也支持约定式路由。约定式路由也叫文件路由,就是不需要手写配置,文件系统即路由,通过目录和文件及其命名分析出路由配置。
在这里插入图片描述
备注:约定式路由要先注释.umirc.ts里的routes配置

路由跳转

import { history } from 'umi';
// 跳转到指定路由
history.push('/list');
// 带参数跳转到指定路由
history.push('/list?a=b');
history.push({
  pathname: '/list',
  query: {
    a: 'b',
  },
});
// 跳转到上一个路由
history.goBack();

示例
Index.tsx

import React, { Component } from 'react'
import { history } from 'umi';
import styles from '@/assets/Style/Index.less'; // css module

export default class Index extends Component {
  // 路由跳转不带参数
  toLogin() {
    history.push('/login');
  }
  // 路由跳转带参数
  toLoginWithParameter() {
    history.push({
      pathname: '/login',
      query: {
        a: 'b',
      },
    })
  }
  render() {
    return (
      <div>
      	<div className={styles.title}>首页</div>
        <div onClick={this.toLogin}>跳转不带参数</div>
        <div onClick={this.toLoginWithParameter}>跳转带参数</div>
      </div>
    )
  }
}

User.tsx

import React, { Component } from 'react'
export default class User extends Component {
  constructor(props: any) {
    super(props);
    console.log(props.location.query) // 打印路由参数
  }
  render() {
    return (
      <div>
        <h1 className={styles.title}>User</h1>
      </div>
    )
  }
}

安装插件集@umijs/preset-react

@umijs/preset-react是包含antd,dva等一系列插件的插件集,由于使用指令npx @umijs/create-umi-app创建的项目会自动添加此插件,所以之后不需要再安装@umijs/preset-react插件集包含的插件,否则启动项目的时候会报错
在这里插入图片描述

umi+antd

安装了插件集@umijs/preset-react之后,无需再做任何配置,直接可以按需加载antd并在组件中使用

import React, { Component } from 'react'
import { history } from 'umi';
import { Button } from 'antd'; // 引入antd组件
import styles from '@/assets/Style/Index.less'; // css module

export default class Index extends Component {
  // 路由跳转不带参数
  toUser() {
    history.push('/User');
  }
  // 路由跳转带参数
  toUserWithParameter() {
    history.push({
      pathname: '/User',
      query: {
        a: 'b',
      },
    })
  }
  render() {
    return (
      <div>
      	<div className={styles.title}>首页</div>
        <Button type="primary" onClick={this.toUser}>跳转不带参数</Button>
        <Button type="primary" onClick={this.toUserWithParameter}>跳转带参数</Button>
      </div>
    )
  }
}

在这里插入图片描述

umi+dva

安装了插件集@umijs/preset-react之后,无需再做任何配置,直接可以使用dva
在这里插入图片描述

  1. 创建models目录以及model文件userInfo.tsx

    import { Effect, ImmerReducer, Reducer, Subscription } from 'umi';
    export interface UserInfoModelState {
      name: string;
      age: number;
    }
    export interface UserInfoModelType {
      namespace: 'userInfo';
      state: UserInfoModelState;
      effects: {
        query: Effect;
      };
      reducers: {
        save: Reducer<UserInfoModelState>;
        changeName: Reducer<UserInfoModelState>;
        // 启用 immer 之后
        // save: ImmerReducer<UserInfoModelState>;
      };
      subscriptions: { setup: Subscription };
    }
    const UserInfoModel: UserInfoModelType = {
      namespace: 'userInfo',
      state: {
        name: '张三',
        age: 20,
      },
      effects: {
        *query({ payload }, { call, put }) {
        },
      },
      reducers: {
        save(state, action) {
          return {
            ...state,
            ...action.payload,
          };
        },
        changeName(state, action) {
          return {
            ...state,
            ...action.payload,
          };
        },
        // 启用 immer 之后
        // save(state, action) {
        //   state.name = action.payload;
        // },
      },
      subscriptions: {
        setup({ dispatch, history }) {
          return history.listen(({ pathname }) => {
            if (pathname === '/') {
              dispatch({
                type: 'query',
              })
            }
          });
        }
      }
    };
    export default UserInfoModel;
    
  2. 组件中使用

  • class组件中使用

    import React, { Component } from 'react'
    import { connect, UserInfoModelState, Loading } from 'umi';
    
    const connect1: any = connect;
    
    @connect1(({ userInfo, loading }: { userInfo: UserInfoModelState; loading: Loading }) => ({
      userInfo,
      // dva-loading可以自动处理loading状态
      loading: loading.models.index,
    }))
    export default class User extends Component<any, any> {
      constructor(props: any) {
        super(props);
        console.log(props);
        this.state = {
          username: props.userInfo.name
        }
      }
      // 调用userInfo模块的reducers里的changeName方法
      private changeName = () => {
        const { dispatch } = this.props;
        dispatch({
            type: 'userInfo/changeName',
            payload:{
              name: '李四'
            }
        })
      }
      render() {
        return (
          <div>
            <div onClick={this.changeName}>更改用户名</div>
            <div className="title">用户名{this.props.userInfo.name}</div>
          </div>
        )
      }
    }
    
  • 函数式组件中使用

    import React, { FC } from 'react';
    import { UserInfoModelState, ConnectRC, Loading, connect } from 'umi';
    interface PageProps {
      userInfo: UserInfoModelState;
      loading: boolean;
    }
    
    const IndexPage: FC<PageProps> = (props) => {
      const handleClick = () => {
        const { dispatch } : any = props;
        dispatch({
          type: 'userInfo/changeName',
          payload:{
            name: '李四'
          }
        })
      }
      return (
        <div>
          <div onClick={handleClick}>更改用户名</div>
          <div className="title">用户名{props.userInfo.name}</div>
        </div>
      )
    };
    export default connect(({ userInfo, loading }: { userInfo: UserInfoModelState; loading: Loading }) => ({
      userInfo,
      loading: loading.models.index,
    }))(IndexPage);
    

    在这里插入图片描述

  • 项目目录
    在这里插入图片描述

主要文章参考
www.icode9.com/content-4-6…
www.cnblogs.com/llcdbk/p/13…
www.cnblogs.com/crazycode2/…
www.jianshu.com/p/86bd9fc0a…
segmentfault.com/q/101000001…
blog.csdn.net/YMX2020/art…
blog.csdn.net/SCU_Cindy/a…

初次尝试,摸坑永不止步,还有以下文章也参考了一下,拓展思路
www.cnblogs.com/jiawei-Wang…
segmentfault.com/a/119000002…
zhuanlan.zhihu.com/p/69200639
www.yuque.com/umijs/umi/d…
www.codercto.com/a/25627.htm…
blog.csdn.net/deletGlobal…
blog.csdn.net/YMX2020/art…
zhuanlan.zhihu.com/p/92617879
www.caotama.com/294655.html
www.cnblogs.com/winfred/p/8…
www.jianshu.com/p/21f8ed30e…
www.cnblogs.com/axel10/p/85…
www.jianshu.com/p/81a5d4371…
blog.csdn.net/sllailcp/ar…
www.cnblogs.com/lucas27/p/9…
www.jianshu.com/p/c1a4166d9…
blog.csdn.net/Leonardo_Zh…
www.jianshu.com/p/21f8ed30e…