Umijs笔记

一、快速上手

文档:https://v3.umijs.org/zh-CN

1. 环境准备

推荐使用 yarn 管理 npm 依赖

npm i yarn tyarn -g

2. 脚手架使用

2.1 新建目录

mkdir [项目名称] && cd [项目名称]

2.2 创建项目

yarn create @umijs/umi-app

2.3 安装依赖

yarn

2.4 启动项目

yarn start

二、目录结构

├── package.json
├── .umirc.ts # 配置文件,包含 umi 内置功能和插件的配置。
├── .env
├── dist
├── mock # 存储 mock 文件,此目录下所有 js 和 ts 文件会被解析为 mock 文件。
├── public # 此目录下所有文件会被 copy 到输出路径。(不会被打包)
└── src
    ├── .umi
    ├── layouts/index.tsx # 约定式路由时的全局布局文件。
    ├── pages # 所有路由组件存放在这里。
        ├── index.less
        └── index.tsx
    └── app.ts # 运行时配置文件,可以在这里扩展运行时的能力,比如动态修改路由、修改 render 方法等。

三、常用配置

1. 配置

Umi 在 .umirc.ts 或 config/config.ts 中配置项目和插件,支持 es6。一份常见的配置如下

如果项目的配置不复杂,推荐在 .umirc.ts 中写配置; 如果项目的配置比较复杂,可以将配置写在 config/config.ts 中,并把配置的一部分拆分出去,比如路由配置可以拆分成单独的 routes.ts

2. hash

配置是否让生成的文件包含 hash 后缀,通常用于增量发布和避免浏览器加载缓存。一般是在执行build命令时,打包的文件会带上文件指纹。

3. base

设置路由前缀,通常用于部署到非根目录。

比如,你有路由 / 和 /users,然后设置了 base 为 /foo/,那么就可以通过 /foo/ 和 /foo/users 访问到之前的路由。

4. publicPath

配置 webpack 的 publicPath。当打包的时候,webpack 会在静态文件路径前面添加 publicPath 的值,当你需要修改静态文件地址时,比如使用 CDN 部署,把 publicPath 的值设为 CDN 的值就可以。如果使用一些特殊的文件系统,比如混合开发或者 cordova 等技术,可以尝试将 publicPath 设置成 ./ 相对路径。

5. outputPath

指定输出路径。

注意:不允许设定为 src、public、pages、mock、config 等约定目录

6. title

配置标题。

比如:

export default {
  title: 'hello umijs',
};

此外,你还可以针对路由配置标题,比如,\

export default {
  title: 'hello umijs',
  routes: [
    { path: '/', title: 'Home' },
    { path: '/users', title: 'Users' },
    { path: '/foo' },
  ],
};

然后我们访问 / 标题是 Home,访问 /users 标题是 Users,访问 /foo 标题是默认的 hello umijs

路由的title会高于去全局的title

7. history

路由模式

  • Type: object
  • Default: { type: 'browser' }

8. targets

配置需要兼容的浏览器最低版本,会自动引入 polyfill 和做语法转换。

比如要兼容 ie11,需配置

export default {
  targets: {
    ie: 11,
  },
};

9. proxy

配置代理能力。

export default {
  proxy: {
    '/api': {
      'target': 'http://jsonplaceholder.typicode.com/',
      'changeOrigin': true,
      'pathRewrite': { '^/api' : '' },
    },
  },
}

注意:proxy 配置仅在 dev 时生效。

10. theme

配置主题,实际上是配 less 变量。

export default {
  theme: {
    '@primary-color': '#1DA57A',
  },
};

11. routes

配置路由,umi 的路由基于 react-router@5 实现,配置和 react-router 基本一致

四、路由

1. 配置路由

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

.umirc.ts文件中:

export default {
  routes: [
    { path: '/', component: '@/pages/index' },
    { path: '/user', component: '@/pages/user' },
  ],
}

2. component

配置 locationpath 匹配后用于渲染的 React 组件路径。可以是绝对路径,也可以是相对路径,如果是相对路径,会从 src/pages 开始找起。如果指向 src 目录的文件,可以用 @,也可以用 ../

3. exact

表示是否严格匹配,即 location 是否和 path 完全对应上。

4. routes

配置子路由,通常在需要为多个路径增加 layout 组件时使用。

export default {
  routes: [
    { path: '/login', component: 'login' },
    {
      path: '/',
      component: '@/layouts/index',
      routes: [
        { path: '/list', component: 'list' },
        { path: '/admin', component: 'admin' },
      ],
    }, 
  ],
}

然后在 src/layouts/index 中通过 props.children 渲染子路由,

export default (props) => {
  return <div>{ props.children }</div>;
}

5. redirect

配置路由跳转。

export default {
  routes: [
    { exact: true, path: '/', redirect: '/list' },
    { exact: true, path: '/list', component: 'list' },
  ],
}

6. wrappers

  • Type: string[]

配置路由的高阶组件封装。比如,可以用于路由级别的权限校验:

export default {
  routes: [
    { path: '/user', component: 'user',
      wrappers: [
        '@/wrappers/auth',
      ],
    },
    { path: '/login', component: 'login' },
  ]
}

然后在 src/wrappers/auth 中,

import { Redirect } from 'umi'

export default (props) => {
  const { isLogin } = useAuth();
  if (isLogin) {
    return <div>{ props.children }</div>;
  } else {
    return <Redirect to="/login" />;
  }
}

这样,访问 /user,就通过 useAuth 做权限校验,如果通过,渲染 src/pages/user,否则跳转到 /login,由 src/pages/login 进行渲染。

7. title

配置路由的标题。

8. 页面跳转

import { history } from 'umi';

// 跳转到指定路由
history.push('/list');

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

// 跳转到上一个路由
history.goBack();

9. 动态路由

export default {
  routes: [
    { path: '/user/:id', component: 'user' }, // 动态路由,id必传
    { path: '/user2/:id?', component: 'user' }, // 动态路由,id可不传
  ],
}

可以通过组件的props获取到路由参数

10. 路由组件参数

路由组件可通过 props 获取到以下属性:

  • match,当前路由和 url match 后的对象,包含 paramspathurlisExact 属性
  • location,表示应用当前处于哪个位置,包含 pathnamesearchquery 等属性
  • history,同 api#history 接口
  • route,当前路由配置,包含 pathexactcomponentroutes
  • routes,全部路由信息

五、约定式路由

除配置式路由外,Umi 也支持约定式路由。约定式路由也叫文件路由,就是不需要手写配置,文件系统即路由,通过目录和文件及其命名分析出路由配置。

如果没有 routes 配置,Umi 会进入约定式路由模式,然后分析 src/pages 目录拿到路由配置。

比如以下文件结构:

.
  └── pages
    ├── index.tsx
    └── users.tsx

会得到以下路由配置,

[
  { exact: true, path: '/', component: '@/pages/index' },
  { exact: true, path: '/users', component: '@/pages/users' },
]

需要注意的是,满足以下任意规则的文件不会被注册为路由:

  • ._ 开头的文件或目录
  • d.ts 结尾的类型定义文件
  • test.tsspec.tse2e.ts 结尾的测试文件(适用于 .js.jsx.tsx 文件)
  • componentscomponent 目录
  • utilsutil 目录
  • 不是 .js.jsx.ts.tsx 文件
  • 文件内容不包含 JSX 元素

六、页面跳转

1. 声明式

通过 Link 使用,通常作为 React 组件使用

import { Link } from 'umi';

export default () => (
  <Link to="/list">Go to list page</Link>
);

2. 命令式

  1. 通过 history 使用,通常在事件处理中被调用。
import { history } from 'umi';

function goToListPage() {
  history.push('/list');
}
  1. 也可以直接从组件的属性props中取得 history
export default (props) => (
  <Button onClick={()=>props.history.push('/list');}>Go to list page</Button>
);

3. history

3.1 history用于获取当前路由信息

import { history } from 'umi';

// history 栈里的实体个数
console.log(history.length);

// 当前 history 跳转的 action,有 PUSH、REPLACE 和 POP 三种类型
console.log(history.action);

// location 对象,包含 pathname、search 和 hash
console.log(history.location.pathname);
console.log(history.location.search);
console.log(history.location.hash);

3.2 history用于路由跳转

import { history } from 'umi';

// 跳转到指定路由
history.push('/list');

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

// 跳转到上一个路由
history.goBack();

3.3 history用于路由监听

import { history } from 'umi';

const unlisten = history.listen((location, action) => {
  console.log(location.pathname);
});
unlisten();

七、HTML模板

在原始的react脚手架或vite创建的项目,会存在一个index.html模板,有个id为root的节点用来挂载组件,但在umijs创建的项目的目录结构中没有index.html模板,该模板放在了umi这个包中。

1. 修改默认模板

新建 src/pages/document.ejs,umi 约定如果这个文件存在,会作为默认模板,比如:

<!doctype html>
<html>
<head>
  <meta charset="utf-8" />
  <title>Your App</title>
</head>
<body>
  <div id="root"></div>
</body>
</html>

2. 配置模板

模板里可通过 context 来获取到 umi 提供的变量,context 包含:

  • route:路由信息,需要打包出多个静态 HTML 时(即配置了 exportStatic 时)有效
  • config:用户配置信息

例如:

<link rel="icon" type="image/x-icon" href="<%= context.config.publicPath %>favicon.png" />

八、Mock数据

1. 约定式 Mock 文件

Umi 约定 /mock 文件夹下所有文件为 mock 文件。

比如:

.
├── mock
    ├── api.ts
    └── users.ts
└── src
    └── pages
        └── index.tsx

/mock 下的 api.tsusers.ts 会被解析为 mock 文件。

2. 编写 Mock 文件

export default {
  // 支持值为 Object 和 Array
  'GET /api/users': { users: [1, 2] },

  // GET 可忽略
  '/api/users/1': { id: 1 },

  // 支持自定义函数,API 参考 express@4
  'POST /api/users/create': (req, res) => {
    // 添加跨域请求头
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.end('ok');
  },
}

3. 引入 Mock.js

import mockjs from 'mockjs';

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

4. 使用

umijs中内置了plugin-request(基于 umi-request 和 umi-hooks 的请求方案)

import { Button } from 'antd';
import { request } from 'umi';

export default function IndexPage() {
  const getData = async () => {
    let res = await request('/list')
    console.log(res);
  };

  return (
    <div>
      <Button type="primary" onClick={getData}>
        获取数据
      </Button>
    </div>
  );
}

5. 如何关闭 Mock?

在默认情况下,umijs默认开启mock

  1. 可以通过配置关闭,在.umirc.ts中:
export default {
  mock: false,
};
  1. 也可以通过环境变量临时关闭
MOCK=none umi dev

九、dva

1. 什么是dva

dva 首先是一个基于 reduxredux-saga 的数据流方案

2. 如何在umijs中使用dva

2.1 如何使用

步骤:

  1. 创建ui组件
  2. 创建model
  3. 将ui组件和model进行连接
1. 在pages/index.tsx
import {connect} from 'umi'

// 1. 创建ui组件
const DvaPage = (props) => {
    console.log(props.list);
    

    return (
        <button onClick={() => props.dispatch({
            type: 'list/fetchList', // 命名空间/方法名
            // payload: null
            payload: { // 传递的参数
                pageNum: 1
            }
        })}>获取list</button>
    )
}

// 3. 将ui组件和model进行连接
// 把list作为props传递给ui组件
export default connect(({list}) => ({list}))(DvaPage)

调用方法

props.dispatch({
    type: 'list/fetchList', // 命名空间/方法名
    // payload: null
    payload: { // 传递的参数
        pageNum: 1
    }
})
2. 在models/list.ts
import {request} from "umi";

const getList = (e) => {
    // yield call(getList, payload)
    // e: payload的参数
    return request('https://jsonplaceholder.typicode.com/posts')
}

// 2. 创建model
export default {
    // 调用model的时候,是通过命名空间去调用,不要和其他的model同名
    namespaces: 'list',

    // 状态,跟react中的state类似,和redux中保存的state一样
    state: {
        list: []
    },

    // 异步操作,跟redux中的reducer类似,但是redux中的reducer只能同步操作,而effects可以异步操作
    effects: {
        *fetchList({payload, callback}, {call, put}) {
            // payload: ui组件传递的数据
            // 获取数据
            const response = yield call(getList, payload)

            // 调用reducers,更新状态
            yield put({
                type: 'updateList', // 类似于redux中action的type
                payload: response // 传递给reducers的数据
            })
        }
    },

    // 同步操作,更新state
    reducers: {
        updateList(state, action) {
            return {
                ...state,
                list: action.payload
            }
        }
    },
}
3. 创建model

约定式的 model 组织方式,不用手动注册 model。

符合以下规则的文件会被认为是 model 文件:

  • src/models 下的文件
  • src/pages 下,子目录中 models 目录下的文件
  • src/pages 下,所有 model.ts 文件(不区分任何字母大小写)

例如:

+ src
  + models/a.ts
  + pages
    + foo/models/b.ts
    + bar/model.ts

其中 a.ts,b.ts 和 model.ts 如果其内容是有效 dva model 写法,则会被认为是 model 文件。

2.1 问题

umi3.x中内置dva,只需要用约定式的model组织方式即可使用。但是在4.x版本后发现无法使用dva了,在配置中加上dva:{}后,运行会报错 Invalid config keys: dva

最主要的原因就是4.x版本不再有3.x支持的插件依赖了。
当然官网上写的很清楚,使用@Umi/Max创建的项目,就会默认安装dva插件,可以按需开启。

解决办法

  1. 安装依赖
npm install --save-dev @umijs/plugins
  1. 在配置文件.umirc.ts或者config.ts文件下,进行显示声明来开启插件
// .umirc.ts or config.ts
export default {
  ...
  plugins: ['@umijs/plugins/dist/dva'],
  dva:{}
};

十、运行时配置

运行时配置和配置的区别是他跑在浏览器端,基于此,我们可以在这里写函数、jsx、import 浏览器端依赖等等,注意不要引入 node 依赖。

1. 配置方式

约定 src/app.tsx 为运行时配置。

2. 配置项

2.1 patchRoutes({ routes })

动态添加路由

比如在最前面添加一个 /foo 路由:

export function patchRoutes({ routes }) {
  // routes为已经注册的路由
  routes.unshift({
    path: '/foo',
    component: require('@/pages/foo').default, // 不能直接写字符串,要写一个组件
  });
}

注意:直接修改routes,不需要返回

2.2 render(oldRender: Function)

覆写 render,会在patchRoutes之前执行

比如用于渲染之前做权限校验:

import { history } from 'umi';

export function render(oldRender) {
  fetch('/api/auth').then(auth => {
    if (!auth.isLogin) {
        history.push('/login');
    }

    // 在渲染前可以进行其他操作
    // ...

    oldRender()
  });
}

也可以和2.1 patchRoutes配合使用,在render中获取动态菜单列表,使用patchRoutes进行渲染。

2.3 onRouteChange({ routes, matchedRoutes, location, action })

在初始加载和路由切换时做一些事情,在patchRoutes之后执行。

  1. 比如用于做埋点统计:
export function onRouteChange({ location, routes, action }) {
  console.log(location.pathname);
}
  1. 比如用于设置标题:
export function onRouteChange({ matchedRoutes }) {
  if (matchedRoutes.length) {
    // 配置路由时,需要配置title
    document.title = matchedRoutes[matchedRoutes.length - 1].route.title || '';
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值