初识umi

一、umi简介

Umi,中文发音为「乌米」,是可扩展的企业级前端应用框架。Umi 以路由为基础的,同时支持配置式路由和约定式路由,保证路由的功能完备,并以此进行功能扩展。然后配以生命周期完善的插件体系,覆盖从源码到构建产物的每个生命周期,支持各种功能扩展和业务需求。

Umi 是蚂蚁集团的底层前端框架,已直接或间接地服务了 10000+ 应用,包括 Java、Node、H5 无线、离线(Hybrid)应用、纯前端 assets 应用、CMS 应用、Electron 应用、Serverless 应用等。他已经很好地服务了我们的内部用户,同时也服务了不少外部用户,包括淘系、飞猪、阿里云、字节、腾讯、口碑、美团等。在 2021 年字节的调研报告中,Umi 是其中 25.33% 开发者的选择。

Umi 有很多非常有意思的特性,比如。

1、企业级,在安全性、稳定性、最佳实践、约束能力方面会考虑更多 2、插件化,啥都能改,Umi 本身也是由插件构成 3、MFSU,比 Vite 还快的 Webpack 打包方案 4、基于 React Router 6 的完备路由 5、默认最快的请求 6、SSR & SSG 7、稳定白盒性能好的 ESLint 和 Jest 8、React 18 的框架级接入 9、Monorepo 最佳实践

官网地址:https://umijs.org/

二、umi环境搭建

1、快速上手

第1步、创建umi项目

首先在终端执行如下命令来创建umi项目

yarn create umi

第2步、项目模板选择

  • simple App:你可以理解为纯净版的Umi,包含umi4文档上关于Guides下的所有功能,但是不包括Umi Max。

  • Ant Design Pro:包含Umi Max的完整功能,均可通过在.umirc.ts 或 config/config.ts中插拔式配置。

  • Vue Simple App:如果你的项目框架是vue则使用该模版。

第3步、选择包管理工具

? Pick Npm Client » - Use arrow-keys. Return to submit.
    npm
    cnpm
    tnpm
>   yarn
    pnpm

第4步、选择资源注册地

? Pick Npm Registry » - Use arrow-keys. Return to submit.
    npm
>   taobao

第5步、启动项目

yarn dev

第6步、运行项目

在浏览器地址栏输入:http://localhost:8000

2、目录结构
.
├── config
│   └── config.ts
├── dist
├── mock
│   └── app.ts|tsx
├── src
│   ├── .umi
│   ├── .umi-production
│   ├── app.ts
│   ├── layouts
│   │   ├── BasicLayout.tsx
│   │   ├── index.less
│   ├── models
│   │   ├── global.ts
│   │   └── index.ts
│   ├── pages
│   │   ├── index.less
│   │   └── index.tsx
│   ├── utils // 推荐目录
│   │   └── index.ts
│   ├── services // 推荐目录
│   │   └── api.ts
│   ├── global.ts
│   ├── global.(css|less|sass|scss)
│   ├── favicon.(ico|gif|png|jpg|jpeg|svg|avif|webp)
│   └── loading.tsx
├── node_modules
│   └── .cache
│       ├── bundler-webpack
│       ├── mfsu
│       └── mfsu-deps
├── .env
├── plugin.ts 
├── .umirc.ts // 与 config/config 文件 2 选一
├── package.json
├── tsconfig.json
└── typings.d.ts
  • .umi:dev 临时目录,需添加到 .gitignore

  • .umirc.ts:配置文件,包含 Umi 内置功能和插件的配置。 config/config.ts 文件功能相同,2 选 1 。.umirc.ts 文件优先级较高

  • config/config.ts:配置文件,包含 Umi 内置功能和插件的配置。 config/config.ts 文件功能相同,2 选 1 。.umirc.ts 文件优先级较高

  • dist:执行 umi build 后,产物默认会存放在这里。可通过配置修改产物输出路径

  • .env:环境变量,用户自己创建的

  • mock:存储 mock 文件,此目录下所有 jsts 文件会被解析为 mock 文件。用于本地的模拟数据服务。

  • public:此目录下所有文件会被 copy 到输出路径。

  • tsconfig.json:ts的配置文件

3、常用配置
3.1、typescript提示

如果想要在配置时也有 Typescript 的语法提示,可以在配置的地方包一层 defineConfig, 这样配置的时候就可以有语法提示了:

//umirc.ts
import {defineConfig} from 'umi'
export default defineConfig({
  npmClient: 'yarn',
});
3.2、常见配置设置
//umirc.ts
import {defineConfig} from 'umi'
export default defineConfig({
  npmClient: 'yarn',
  title:'Hello UMI4',//配置标题
  favicons:['/favicon.ico'],  //配置favicon使用本地图片,图片放在public目录下
});
3.2、环境变量配置

在项目根目录下的.env目录下配置,常见环境变量配置有修改服务器的端口号。

//.env
port=4000

三、路由配置

Umi 中关于路由的配置,有两种方式:

  • 配置式路由:在配置文件 .umirc.ts 中通过代码对路由进行相关的配置;

  • 约定式路由:不需要通过代码去手写路由配置,只需要按照 Umi 的约定去创建项目页面的目录,Umi 会自动解析出路由的配置;

在一个项目中,配置式路由和约定式路由只能任选其一。

1、配置式路由(掌握)
1.1、配置一二级路由

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

import { defineConfig } from 'umi';

export default defineConfig({
  routes: [
    { path: '/login', component: '@/pages/login'},
    { path:'/register',component:'@/pages/register'},
    {
      path: '/', component: '@/pages/index',
      routes: [
        { path: '/student', component: '@/pages/student'},
        { path: '/director', component: '@/pages/director' }
      ]
    }
  ]
});
1.2、配置路由出口

然后在 src/layouts/index 中通过 <Outlet/> 渲染子路由,配置一级路由出口

import {Outlet } from 'umi';
export default function Layout() {
  return (
    <div>
      <Outlet />
    </div>
  );
}

然后再在src/pages/index中配置二级路由的出口

import {Outlet} from 'umi'
import './index.less'
export default function index() {
  return (
    <div className='box'>
        <div className='navs'>
           <ul>
            <li>学生管理</li>
            <li>班主任管理</li>
           </ul>
        </div>
        <div className='main'>
          <Outlet></Outlet>
        </div>
    </div>
  )
}
1.3、配置路由模式

Umi 项目中,路由默认history 模式,如果要更改为 hash 模式,可以在配置文件 .umirc.ts 中添加以下属性:

export default defineConfig({    
    history: {
        type: 'hash'
    },
    // ...
});
2、路由跳转

Umi 中,将路由的跳转方式分为“声明式”和“命令式”,实际上对应的就是标签跳转和事件跳转。

2.1、声明式

通过<Link>组件方式进行跳转

import {Link, Outlet} from 'umi'
import './index.less'
export default function index() {
  return (
    <div className='box'>
        <div className='navs'>
           <ul>
            <li><Link to="/student">学生管理</Link></li>
            <li><Link to="/direct">班主任管理</Link></li>
           </ul>
        </div>
        <div className='main'>
          <Outlet></Outlet>
        </div>
    </div>
  )
}
2.2、命令式

通过 history 使用,通常在事件处理中被调用

import {history} from 'umi'
export default function login() {
  const toRegister=(e:any)=>{
    e.preventDefault()
    history.push('/register')
  }
  return (
    <div>
      <h1>登录</h1>
      <a href="#" onClick={toRegister}>没有账号,请注册</a>
    </div>
  )
}
3、路由传参
3.1、query传参
  • 跳转时传递参数

<Link to="/路径?参数名=参数值">跳转</Link>
history.push('/路径?参数名=参数值');
  • 组件中接收参数

import {useSearchParams} from 'umi'
const[searchParams,setSearchParams]=useSearchParams()
searchParams.get(参数名)
3.2、动态路由传参
  • 跳转时传递参数

history.push('/路径/参数')
  • 配置动态路由

.umirc.ts 文件中,更改动态路由的配置:

{ path: '/路径/:变量名', component: '组件路径' },
  • 在组件中获取参数

import { useParams } from 'umi';
const params = useParams();
4、路由的其他配置
4.1、路由重定向
[
    {
      path:'/',redirect:'/home'
    }
]
4.2、404页面配置

在umi中,只需要在所有路由配置的最后,添加一个关于404页面的配置

[
    {
      path:'*',
      component:'@/pages/notfound'
    }
]

四、Mock数据和网络请求

Mock数据是前后端开发过程中必不可少的一环,是前后端开发的关键链路,由于前后端开发过程中前端要使用到后端的数据,但是如果后端api没有开发出来,前端不会要等后端API才能开发,这样在开中会调职前端工作的阻塞,为了解决这个问题可以采用Mock数据的方式。

什么是 Mock 数据:在前后端约定好 API 接口以后,前端可以使用 Mock 数据来在本地模拟出 API 应该要返回的数据,这样一来前后端开发就可以同时进行,不会因为后端 API 还在开发而导致前端的工作被阻塞。

Umi 提供了开箱即用的 Mock 功能,能够用方便简单的方式来完成 Mock 数据的设置。

1、目录约定

Umi 约定 /mock 目录下的所有文件为 Mock 文件,例如这样的目录结构

.
├── mock
    ├── todos.js
    ├── items.js
    └── users.js
└── src
    └── pages
        └── index.js
2、Mock文件

Mock 文件默认导出一个对象,而对象的每个 Key 对应了一个 Mock 接口,值则是这个接口所对应的返回数据,例如这样的 Mock 文件:

export default{
    'GET /api/goods':[
        {id:'1001',name:'曲奇饼干',price:23.5},
        {id:'1002',name:'德芙巧克力',price:63.2},
        {id:'1003',name:'大列巴面包',price:13.5}
    ]
}

当 Http 的请求方法是 GET 时,可以省略方法部分,只需要路径即可

3、自定义函数

除了直接静态声明返回值,也可以用函数的方式来声明如何计算返回值

export default {
    'POST /api/login': (req, res) => {
        const { username, password } = req.body
        if (username == "Giles" && password == "123456") {
            res.send({
                code: 1,
                message: '登录成功'
            })
        } else {
            res.send({
                code: 0,
                message: '登录失败'
            })
        }
    }
}
4、引入Mock.js

在 Mock 中我们经常使用 Mock.js 来帮我们方便的生成随机的模拟数据,如果你使用了 Umi 的 Mock 功能,建议你搭配这个库来提升模拟数据的真实性:

首先在终端执行命令安装mockjs

yarn add mockjs

然后再在mock/citys.js目录下编写如下的代码,实现使用mockjs模拟mock数据

import mockjs from 'mockjs';
export default {
    'GET /api/tags': mockjs.mock({
        'list|100': [{ name: '@city', 'value|1-100': 50, 'type|0-2': 1 }],
    })
}

五、div状态机

1、dva状态机的工作流程

Umi 内置了 Dva 提供了一套状态管理方案:

2、Model配置
  • 在src目录下新建models文件夹,然后以购物车为例,在 src/models 目录中创建一个 shopcartModel.ts 文件

    src |--- models | |--- shopcartModel.ts
  • 配置dva

    首先要安装 @umijs/plugins

yarn add -D @umijs/plugins

然后需要在.umirc.ts配置文件中配置plugins和dva节点

// .umirc.ts
export default {
  npmClient: 'yarn',
  plugins: [
    '@umijs/plugins/dist/model',
    '@umijs/plugins/dist/dva',
  ],
  dva:{}
}
3、具体实现步骤
  • model的基本结构

    每一个仓库模块对象中,都有如下基本配置

const shopcartModel = {
    // 状态机模块名称(如果没有设置该属性,默认当前文件名为模块名)
    namespace: 'shopcart',
    // 数据
    state: {},
    // 修改数据的方法
    reducers: {},
    // 异步方法
    effects: {}
}
  • 设置数据初始化

const shopcartModel = {
    // 状态机模块名称(如果没有设置该属性,默认当前文件名为模块名)
    namespace: 'shopcart',
    // 数据
    state: {
        //定义初始数据
        rows:[]
    },
    // 修改数据的方法
    reducers: {},
    // 异步方法
    effects: {}
}
  • 设置修改数据的方法

const shopcartModel = {
    // 状态机模块名称(如果没有设置该属性,默认当前文件名为模块名)
    namespace: 'shopcart',
    // 数据
    state: {
        //定义初始数据
        rows:[]
    },
    // 修改数据的方法(同步的)
    reducers: {
       setShopcartList(state,{payload}){
           return{
            ...state,
            rows:payload
           }
        }
    },
    // 异步方法
    effects: {}
}
  • 设置异步方法

import $http from '../api/http'
const shopcartModel = {
    // 状态机模块名称(如果没有设置该属性,默认当前文件名为模块名)
    namespace: 'shopcart',
    // 数据
    state: {
        //定义初始数据
        rows:[]
    },
    // 修改数据的方法(同步的)
    reducers: {
        setShopcartList(state,{payload}){
            state.rows=payload.rows	
        }
    },
    // 异步方法
    effects: {
       *getShopcartListAsync({payload},{call,put}){
           const result=yield call($http.shopcartList.getShopcartList)
           yield put({type:'setShopcartList',payload:result.data.data})
        }
    }
}
export default shopcartModel

3、组件中使用操作仓库

使用之前需要安装react-redux包

yarn add react-redux
import {useEffect, useState} from 'react'
import {useSelector,useDispatch} from 'react-redux'
export default function student() {
  const dispatch=useDispatch()
  useEffect(()=>{
    dispatch({
      type:'shopcart/getShopcartListAsync'
    })
  },[])
  const list=useSelector((state)=>{
    return state.shopcart.rows
      
  })
  return (
    <div>
      <table>
        <thead>
          <tr>
            <td>编号</td>
            <td>名称</td>
            <td>价格</td>
            <td>数量</td>
            <td>小计</td>
          </tr>
        </thead>
        <tbody>
          {
            list.map(item=><tr key={item._id}>
              <td>{item._id}</td>
              <td>{item.name}</td>
              <td>{item.price}</td>
              <td>
                <button>-</button>
                {item.num}
                <button>+</button>
              </td>
              <td>{item.price*item.num}</td>
            </tr>)
          }
          </tbody>
      </table>
    
    </div>
  )
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值