美文网首页
阿里出品的Umi框架,你看懂了吗?

阿里出品的Umi框架,你看懂了吗?

作者: oh_roy | 来源:发表于2020-03-06 18:27 被阅读0次

背景

image.png

之前GMTC 全球大前端技术大会上,公开了这张阿里前端框架的时间线,我们可以看到,在2017.8和2017.12,阿里两个不同的业务部门曾接连推出了两款前端框架,Umi和Bigfish。但是在2018.6,阿里对两个部门进行了整合,产出了Umi+Bigfish这样的整合框架。在2019.11月,Umi这款框架才算最终落地,经过了两年的整合,我们来看看这个Umi框架到底是个什么黑科技产品

它是什么

image.png
从大会上放出的另一张图我们看到了,我们在“刀耕火种”的时代做前端开发的时候,总是会考虑我们选择在项目里使用 React 库来开发的时候,项目的数据流方案应该怎么选择(redux, mobx, redux-saga, redux-router,dva,等),组件库 的选择(antd, material design),以及webpack打包的复杂配置等,这里的 Bigfish&um看起来像是把以前我们项目中通常会用到的一些库整合到了一起。
既然Umi是一个框架,那就有很多“约定”的规则,需要我们遵守,不管是在路由配置,还是在mock数据,以及数据和页面交互方面,我们都能够感受到这种潜在的“约定”

它有什么

Umi概述
幕布版链接
之前用幕布整理了一个其中大概的内容,而且2.x与3.x版本差别还比较大,这张图片整理的大部分是基于2.x的版本,可以粗略的看看,也可以点击链接去官网查看,Umi官网

它怎么用

我这里用Umi做了一个很简陋的应用,不过基本需要包括的路由,异步请求,页面跳转和mock数据都用到了,这里就拆分一下给大家做个演示。

登陆
image.png
这是一个很正常的登陆表单,表单的UI和验证部分都是直接从Umi提供的工作台上找到的基于antd的区块,添加进项目就可以用,非常方便,只需要修改表单登陆事件处理的函数:
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    this.props.form.validateFields((err: any, values: any) => {
      if (!err) {
        this.props.dispatch({
          type: 'users/login',
          payload: values,
        });
      }
    });
  };

这里就要提到Umi推荐使用的数据流方案Dva,它封装并简化了redux、redux-router、redux-saga的使用难度,具体想了解更多Dva的更多内容,可以访问Dva官网
在代码中,可以看到这里使用了redux常用的dispatch action的方式来发起登录请求。所以我们需要有一个地方能够handle这个action的执行,接下来看对应的modal是如何实现的:

export default {
  namespace: 'users',
  state: {
    id: '',
    userName: '',
    friends: [],
  },

  reducers: {
    save: (state: UserState, { payload }: { payload: UserState }) => {
      return { ...state, ...payload };
    },
  },

  effects: {
    *login({ payload }: { payload: LoginRequestParams }, { call, put }: EffectsCommandMap) {
      const response = yield call(loginApi, payload)
      yield put({ type: 'save', payload: response });
      router.push('/users');
    }
  },
};

在modal中我们定义了这个modal的namespace叫做users,里面存放的数据包含id,userNamefriends,定义了一个叫做savereducer来保存相关数据,还定义了一个叫做login的方法来做含有副作用的操作,可以看到方法中接收到payload以后调用了call方法来执行了一个loginApi,数据获取成功后,发起save操作来保存返回的数据,然后通过router.push来更改路由。其中有很多Dva相关的知识,感兴趣的朋友可以去了解下。再贴上loginApi中做的事情:

export const loginApi = (payload: LoginRequestParams) => {
    return fetch("http://localhost:8000/api/users", {
        method: "POST",
        body: JSON.stringify(payload)
    }).then((response: Response) => response.json())
};

这里直接请求了localhost接口的原因是我并没有做后端Api,所以是用了Umi本身提供的mock功能使用mock数据来模拟应用的后端访问。至此,一个完整的login登录流程就出来了。当我们点击login的时候,打开Chrome的redux插件

redux插件
store变化

可以看到这里有我们完整的action flow,以及我们的state的变化。

当我们将数据存储起来以后,我们在组件中如何使用store中的数据的方式和redux类似,通过dva提供的connect方法,传入mapStateToProps参数即可,如:

const mapStateToProps = ({ users }: { users: UserState }) => ({
  userName: users?.userName,
  friends: users?.friends,
});

export default connect(mapStateToProps)(WelcomePage);
路由

在上面的代码已经看到了我们路由跳转的一种方法是通过Umi提供的router.push(url),官方还提供一种是通过链接跳转,<Link to="/user">这种方式。
在Umi中有两种方式来定义路由,一种是约定式,一种是配置式。接下来就说一下这两种的区别:

约定式
src目录结构
我们在使用脚手架生成Umi项目后可以看到src的目录里面包含了一个pages的文件夹,umi打包的时候会将目录之间的关系映射成路由关系,如:
  • src/pages/users/index.tsx 会成为 /users
  • src/pages/users/$id.tsx 会成为 /users/:id
  • src/pages/users/[id]/settings.tsx 会成为 /users/:id/settings
    以此类推。umi打包后生成的pages/.umi/router.js文件也可以看出来它们之间的关系,如图:
...,
routes: [
      {
        path: '/404',
        exact: true,
        component: __IS_BROWSER
          ? _dvaDynamic({
              component: () => import('../404.tsx'),
            })
          : require('../404.tsx').default,
        _title: 'umi-js',
        _title_default: 'umi-js',
      },
      {
        path: '/',
        exact: true,
        component: __IS_BROWSER
          ? _dvaDynamic({
              component: () => import('../index.tsx'),
            })
          : require('../index.tsx').default,
        _title: 'umi-js',
        _title_default: 'umi-js',
      },
配置式

如果项目路由足够复杂,有很多权限校验的东西,那么Umi也提供了配置化修改。在项目中会有一个.umirc.ts的文件,这个文件就是项目的配置文件,也可以通过config/config.ts文件来配置。


const config: IConfig = {
  routes: [
    { path: '/', component: './index' },
    {
      path: '/users',
      component: './users/index',
    },
    {
      path: 'users/detail',
      component: './users/detail',
    },
    { path: 'users/:id', component: './users/$id.tsx' },
  ],
  ...,
};

在配置文件里我们可以更灵活的定义我们路由,也可以添加权限路由和嵌套路由,这里就不赘述了,有兴趣可以访问官网查看。

mock数据

当我们做前后端分离项目的时候,前后端通常不会一直都以相同速率开发,总是会有一方等待另一方集成的问题。在umi中针对这种情况给了mock数据的解决方案,如图:


根目录

在根目录中有一个叫做mock的文件夹,Umi约定在这个文件夹下的文件都被视作mock数据。当前端访问本地接口的时候,就会到这里面来对应的查找有没有和请求的url能够匹配的数据,如果有就直接返回。
比如我的api.js文件中:

import mockjs from 'mockjs';
export default {
  'POST /api/users': mockjs.mock({
    userName: '@name',
    id: '@id',
    'friends|5-10': [{ id: '@id', userName: '@name' }],
  }),
};

这里使用了mockjs来生成对应格式的mock数据,这样可以最大限度的保证数据的有效性。

Umi-UI

这里有一个Umi的工作台觉得需要提出来说一下,因为个人觉得这个工作台很大程度上减少了前端开发的门槛。


配置模块
任务模块
资产模块

从上面的图可以看出,如果你不熟悉webpack,不熟悉配置式路由,不熟悉npm script,在这个工作台都能够可视化修改。甚至你可以直接把区块和模板里的东西直接引入到你自己的项目中,无须做太多改动就可用,极大减轻了UI开发的压力。

结语

以上就是对Umi框架的一些简单的介绍,可能大家觉得Umi 2.x有点臃肿,包含的东西太多。没关系,可以尝试Umi 3.0,更加的精简和插件化。

最后附上项目的github地址

相关文章

网友评论

      本文标题:阿里出品的Umi框架,你看懂了吗?

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