Umi,中文可发音为乌米,是可扩展的企业级前端应用框架。Umi 以路由为基础的,同时支持配置式路由和约定式路由,保证路由的功能完备,并以此进行功能扩展。然后配以生命周期完善的插件体系,覆盖从源码到构建产物的每个生命周期,支持各种功能扩展和业务需求。
1️⃣了解Umi |
你可以将Umi简单的理解为一个专注性能的类 next.js 前端框架,并通过约定、自动生成和解析代码等方式来辅助开发,减少我们开发者的代码量。
功能特点 |
- 开箱即用,Umi 内置了路由、构建、部署、测试等,仅需一个依赖即可上手开发。
- 完善的插件体系,Umi 实现了完整的生命周期,并使其插件化。此外还提供针对 React 的集成插件集。
- 完备路由,同时支持配置式路由和约定式路由,同时保持功能的完备性,比如动态路由、嵌套路由、权限路由等等。
技术收敛 |
蚂蚁金服前端框架
Umi把大家常用的技术栈进行整理,收敛到一起,让大家只用 Umi 就可以完成 80% 的日常工作。
Bigfish 企业级前端开发框架,与常见的前端框架和工具不同,它从工程角度集成了各类功能,从初始化开发到最终上线整套技术方案,解决了前端开发经常遇到的前端技术栈整合配置麻烦、开发联调麻烦、前端资源发布上线麻烦三大痛点。
创建第一个Umi应用 |
# 使用 yarn 管理 npm 依赖 使用国内源 之后使用tyarn代替yarn即可
$ npm i yarn tyarn -g
# 创建目录
$ mkdir myapp
# 进入目录
$ cd myapp
# 通过官方工具创建项目
$ yarn create @umijs/umi-app
# 安装依赖
$ yarn
# 启动开发
$ yarn start
启动页面效果 和 当前项目结构
.umi 临时文件 |
.umi 临时目录是整个 Umi 项目的发动机,你的入口文件、路由等等都在这里,这些是由 umi 内部插件及三方插件生成的。
+ .umi
+ core # 内部插件生成
+ pluginA # 外部插件生成
+ presetB # 外部插件生成
+ umi.ts # 入口文件
临时文件是 Umi 框架中非常重要的一部分,框架或插件会根据你的代码生成临时文件,这些原来需要放在项目里的脏乱差的部分都被藏在了这里。
你可以在这里调试代码,但不要在 .git 仓库里提交他,因为他的临时性,每次启动 umi 时都会被删除并重新生成。
修改配置换个页面布局 |
默认的脚手架内置了 @umijs/preset-react,包含布局、权限、国际化、dva、简易数据流等常用功能。
想使用 ant-design-pro 的布局
- 添加
@ant-design/pro-layout
依赖
yarm add @ant-design/pro-layout
- 编辑
.umirc.ts
文件,加入布局配置项+layout:{},
import { defineConfig } from 'umi';
export default defineConfig({
nodeModulesTransform: {
type: 'none',
},
layout:{},
routes: [
{ path: '/', component: '@/pages/index' },
],
fastRefresh: {},
});
部署发布 |
📌构建
$ yarn build
构建产物默认生成到 ./dist 下
📌本地验证
发布之前,可以通过 serve
做本地验证,
# 安装serve服务
npm i serve -g
# 启动发布的项目包
serve ./dist
访问 http://localhost:5000,正常情况下应该是和执行 yarn start 时是一致的。
2️⃣Umi基础 |
目录结构 |
一个基础的 Umi 项目大致是这样的
.
├── package.json 包含插件和插件集
├── .umirc.ts 配置文件,包含 umi 内置功能和插件的配置。
├── .env 环境变量。比如:PORT=8888
├── dist 打包产物默认会存放在这里。
├── mock 存储 mock 文件,此目录下所有 js 和 ts 文件会被解析为 mock 文件。
├── public 此目录下所有文件会被 copy 到输出路径。
├── config 项目复杂时候独立出来的的子配置文件,比如:routes.ts
└── src
├── .umi 临时文件目录,比如入口文件、路由等,都会被临时生成到这里。
├── layouts/index.tsx 约定式路由时的全局布局文件。
├── pages 所有路由组件存放在这里。
├── index.less
└── index.tsx
└── app.ts 运行时配置文件,可以在这里扩展运行时的能力,比如修改路由、修改 render 方法等。
配置 |
Umi 在 .umirc.ts 中配置项目和插件,支持 es6。一份常见的配置如下,
export default {
base: '/docs/',
publicPath: '/static/',
hash: true,
history: {
type: 'hash',
},
}
defineConfig 让配置有代码提示 |
使用defineConfig
方法可以让我们在写配置的时候有代码提示
import { defineConfig } from 'umi';
export default defineConfig({
routes: [
{ path: '/', component: '@/pages/index' },
],
});
本地临时配置 |
可以新建 .umirc.local.ts
,这份配置会和 .umirc.ts
做 deep merge 后形成最终配置。
注:.umirc.local.ts
仅在 umi dev 时有效。umi build 时不会被加载。
// .umirc.ts
export default { a: 1, b: 2 };
// .umirc.local.ts
export default { b: 3,c: 'local' };
拿到的配置是:
{
a: 1,
b: 3,
c: 'local',
}
.local.ts
是本地验证使用的临时配置,请将其添加到.gitignore
,务必不要提交到 git 仓库中.local.ts
配置的优先级最高,比 UMI_ENV 指定的配置更高
多环境多份配置 |
可以通过环境变量 UMI_ENV 区分不同环境来指定配置。
// .umirc.js 或者 config/config.js
export default { a: 1, b: 2 };
// .umirc.cloud.js 或者 config/config.cloud.js
export default { b: 'cloud', c: 'cloud' };
// .umirc.local.js 或者 config/config.local.js
export default { c: 'local' };
不指定 UMI_ENV 时,拿到的配置是:
{
a: 1,
b: 2,
c: 'local',
}
指定 UMI_ENV=cloud 时,拿到的配置是:
{
a: 1,
b: 'cloud',
c: 'cloud',
}
📌如何指定UMI_ENV?
在.env
文件配置
运行时配置 |
运行时配置和配置的区别是前者跑在浏览器端而后者是在服务器端,所以运行时配置:
- 不需要引入 node 依赖
- 可以写函数、jsx、import 浏览器端依赖
配置方式 |
约定(俗成) src/app.tsx
为运行时配置。在 src/app.ts 中你可以配置一些运行时的配置项来实现部分自定义需求。
📌 request 运行时配置示例:
import { RequestConfig } from 'umi';
export const request: RequestConfig = {
timeout: 1000,
errorConfig: {},
middlewares: [],
requestInterceptors: [],
responseInterceptors: [],
};
📌dva 运行时配置示例:
import { createLogger } from 'redux-logger';
import { message } from 'antd';
export const dva = {
config: {
onAction: createLogger(),
onError(e: Error) {
message.error(e.message, 3);
},
},
};
路由 |
在 Umi 中,应用都是单页应用,页面地址的跳转都是在浏览器端完成的,不会重新请求服务端获取 html,html只在应用初始化时加载一次。所有页面由不同的组件构成,页面的切换其实就是不同组件的切换,你只需要在配置中把不同的路由路径和对应的组件关联上。
配置路由 |
在配置文件中进行配置,格式为路由信息的数组。
// config/routes.tsx
export default [
{ exact: true, path: '/', component: 'index' },
{ exact: true, path: '/user', component: 'user' },
]
📌 path [Type: `string` ]
也可以是路径正则表达式
📌 component [Type: string
]
配置 location 和 path 匹配后用于渲染的 React 组件路径。可以是绝对路径,也可以是相对路径,如果是相对路径,会从 src/pages
开始找起。
如果指向 src 目录的文件,可以用 @
,也可以用 ../
。比如 component: '@/layouts/basic'
,或者 component: '../layouts/basic'
,推荐用前者。
📌 exact [Type: `boolean` Default: `true`]
表示是否严格匹配,即 location 是否和 path 完全对应上。
export default {
routes: [
// url 为 /one/two 时匹配失败
{ path: '/one', exact: true },
// url 为 /one/two 时匹配成功
{ path: '/one' },
{ path: '/one', exact: false },
],
}
📌 routes
配置子路由,通常在需要为多个路径增加 layout 组件时使用。
// config/routes.tsx
export default [
{
path: '/ex', component: '@/pages/layouts', routes: [
{ path: '/ex/list', component: '@/pages/list' },
{ path: '/ex/admin', component: '@/pages/admin' }
]
},
]
然后在 src/layouts/index
中通过 props.children
渲染子路由,
// src/layouts/index.tsx
export default (props)=>{
return <>{props.children}</>
}
这样,访问 /ex/list
和 /ex/admin
就会带上 src/layouts/index
这个 layout 组件。
// src/pages/list.tsx
export default function list(){
return (<h1>list</h1>);
}
// src/pages/admin.tsx
export default function admin(){
return (<h1>admin</h1>);
}
📌 redirect [Type: string
]
配置路由跳转。
export default [
{ exact: true, path: '/', redirect: '/list' },
{ exact: true, path: '/list', component: '@/pages/list' }
]
访问 /
会跳转到 /list
,并由 src/pages/list
文件进行渲染。
📌 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
进行渲染。
📌 title [Type: string[]
]
配置路由的标题。
页面跳转 |
命令式页面跳转:通过 history 使用,通常在事件处理中被调用。
import { history } from 'umi';
// 跳转到指定路由
history.push('/list');
// 带参数跳转到指定路由
history.push('/list?a=b');
history.push({
pathname: '/list',
query: {
a: 'b',
},
});
// 跳转到上一个路由
history.goBack();
Link 组件 |
声明式页面跳转
import { Link } from 'umi';
export default () => (
<div>
<Link to="/users">Users Page</Link>
</div>
);
Link 只用于单页应用的内部跳转,如果是外部地址跳转请使用 a
标签
约定式路由 |
约定式路由也叫文件路由,就是不需要手写配置,文件系统即路由,通过目录和文件及其命名分析出路由配置。
如果没有 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.ts、spec.ts、e2e.ts 结尾的测试文件(适用于 .js、.jsx 和 .tsx 文件)
- components 和 component 目录
- utils 和 util 目录
- 不是 .js、.jsx、.ts 或 .tsx 文件
- 文件内容不包含 JSX 元素
动态路由 |
约定 []
包裹的文件或文件夹为动态路由。
比如:
src/pages/users/[id].tsx
会成为/users/:id
src/pages/users/[id]/settings.tsx
会成为/users/:id/settings
动态可选路由 |
约定 [ $]
包裹的文件或文件夹为动态可选路由。
比如:
src/pages/users/[id$].tsx
会成为/users/:id?
src/pages/users/[id$]/settings.tsx
会成为/users/:id?/settings
嵌套路由 |
Umi 里约定目录下有 _layout.tsx 时会生成嵌套路由,以 _layout.tsx 为该目录的 layout。layout文件需要返回一个 React 组件,并通过 props.children 渲染子组件。
// _layout.tsx
export default (props: { children: any })=>{
return <>layout{props.children}</>
}
全局 layout |
约定 src/layouts/index.tsx
为全局路由。返回一个 React 组件,并通过 props.children 渲染子组件。
export default (props: { children: any })=>{
return <>
<h1>全局</h1>
{props.children}</>
}
不同的全局 layout |
在 src/layouts/index.tsx 中对 location.path 做区分,渲染不同的 layout 。
export default function(props) {
if (props.location.pathname === '/login') {
return <SimpleLayout>{ props.children }</SimpleLayout>
}
return (
<>
<Header />
{ props.children }
<Footer />
</>
);
}
404 路由 |
约定 src/pages/404.tsx
为 404 页面,需返回 React 组件。
权限路由 |
通过指定高阶组件 wrappers 达成效果
如下,src/pages/user
:
import React from 'react'
function User() {
return <>user profile</>
}
User.wrappers = ['@/wrappers/auth']
export default User
然后在 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
进行渲染。
插件 |
📌插件的 id 和 key
每个插件都会对应一个 id 和一个 key,id 是路径的简写,key 是进一步简化后用于配置的唯一值。
比如插件 /node_modules/@umijs/plugin-foo/index.js
,通常来说,其 id 为 @umijs/plugin-foo
,key 为 foo
。
启用插件 |
📌package.json 依赖
Umi 会自动检测 dependencies 和 devDependencies 里的 umi 插件,比如:
{
"dependencies": {
"@umijs/preset-react": "1"
}
}
那么 @umijs/preset-react 会自动被注册,无需在配置里重复声明。
📌配置 .umirc.ts
在配置里可通过 presets 和 plugins 配置插件,比如:
export default {
presets: ['./preset', 'foo/presets'],
plugins: ['./plugin'],
}
禁用插件 |
📌配置 key 为 false
export default {
mock: false,
}
检查插件注册情况 |
$ umi plugin list
# 顺便看看他们分别用了哪些 key
$ umi plugin list --key
拓展:按需加载插件 |
常见使用场景:组件体积太大,不适合直接计入 bundle 中,以免影响首屏加载速度。例如:某组件 HugeA 包含巨大的实现 / 依赖了巨大的三方库,且该组件 HugeA 的使用不在首屏显示范围内,可被单独拆出。这时候,dynamic
就该上场了
启用按需加载 |
按需加载功能默认是关闭的,需要通过配置开启
export default {
dynamicImport: {},
}
使用按需加载 |
dynamic
是一个按需加载组件,封装了使用一个异步组件需要做的状态维护工作
📌封装一个异步组件
//AsyncHugeA.tsx
import { dynamic } from 'umi';
export default dynamic({
loader: async function() {
// 这里的注释 webpackChunkName 可以指导 webpack 将该组件 HugeA 以这个名字单独拆出去
const { default: HugeA } = await import(/* webpackChunkName: "external_A" */ './HugeA');
return HugeA;
},
});
📌使用异步组件
import React from 'react';
import AsyncHugeA from './AsyncHugeA';
// 像使用普通组件一样即可
// dynamic 为你做:
// 1. 异步加载该模块的 bundle
// 2. 加载期间 显示 loading(可定制)
// 3. 异步组件加载完毕后,显示异步组件
export default () => {
return <AsyncHugeA />;
}
HTML模板 |
修改默认模板 |
新建 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>
修改title,直接在.umirc.ts
加入title配置项即可
配置模板 |
模板里可通过 context 来获取到 umi 提供的变量,context 包含:
- route,路由信息,需要打包出多个静态 HTML 时(即配置了 exportStatic 时)有效
- config,用户配置信息
.umirc.ts
<link rel="icon" type="image/x-icon" href="<%= context.config.publicPath %>favicon.png" />
Mock 数据 |
约定式 Mock 文件 |
Umi 约定 /mock 文件夹下所有文件为 mock 文件。
.
├── mock
├── api.ts
└── users.ts
/mock 下的 api.ts 和 users.ts 会被解析为 mock 文件。
编写 Mock 文件 |
'requestType requestPath':response
如 /mock/api.ts 的内容如下,
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');
},
}
引入 Mock.js |
yarn add @types/mockjs -D
import mockjs from 'mockjs';
export default {
// 使用 mockjs 等三方库
'GET /api/tags': mockjs.mock({
'list|100': [{ name: '@city', 'value|1-100': 50, 'type|0-2': 1 }],
}),
};
常用的环境变量 |
Umi 中约定根目录下的 .env 为环境变量配置文件。通过key=value
配置
//.env
PORT=3000
HTTPS=1
ANALYZE |
用于分析 bundle 构成,默认关闭。
ANALYZE=1
FORK_TS_CHECKER |
默认不开启 TypeScript 类型检查,值为 1 时启用。比如:
FORK_TS_CHECKER=1
HTTPS |
localhost 开启 https
HTTPS=1
同时也可以使用配置 https: { key: '/path/key.pem', cert: '/path/cert.pem' }
自定义证书。
PORT |
指定端口号,默认是 8000。
UMI_ENV |
指定不同环境各自的配置文件
UMI_ENV=cloud
// .umirc.js
export default { a: 1, b: 2 };
// .umirc.cloud.js
export default { b: 'cloud', c: 'cloud' };
// .umirc.local.js
export default { c: 'local' };
指定 UMI_ENV=cloud 时,拿到的配置是:
{
a: 1,
b: 'cloud',
c: 'cloud',
}
3️⃣样式和资源文件 |
以下虽然使用.css
示例,写成.less
效果一样
使用 CSS |
Umi 内置支持 less
,不支持 sass 和 stylus,但如果有需求,可以通过 chainWebpack 配置或者 umi 插件的形式支持。
全局样式 |
Umi 中约定 src/global.css
为全局样式,如果存在此文件,会被自动引入到入口文件最前面。
CSS Modules |
Umi 会自动识别 CSS Modules 的使用,你把他当做 CSS Modules 用时才是 CSS Modules。
// CSS Modules
import styles from './foo.css';
// 非 CSS Modules
import './foo.css';
使用图片 |
JS 里使用图片 |
import logoSrc from './logo.svg'
<img src={'http://www.ivotoo.com/test/1.jpg'}/>
<img src={require('../../public/images/1.jpeg')}/>
<img src={logoSrc} />
//支持别名,比如通过 @ 指向 src 目录:
<img src={require('@/foo.png')} />
如果是svg类型,还可以组件式引入:
import { ReactComponent as Logo } from './logo.svg'
function Analysis() {
return <Logo width={90} height={120} />
}
CSS 里使用图片 |
通过相对路径引用:
.logo {
background: url(./foo.png);
}
CSS 里也支持别名,但需要在前面加 ~ 前缀:
.logo {
background: url(~@/foo.png);
}
图片路径问题 |
项目中使用图片有两种方式,
- 先把图片传到 cdn,然后在 JS 和 CSS 中使用图片的绝对路径
- 把图片放在项目里,然后在 JS 和 CSS 中通过相对路径的方式使用
前者不会有任何问题;后者,如果在 JS 中引用相对路径的图片时,在发布时会根据 publicPath
引入绝对路径,所以就算没有开启 dynamicImport
时,也需要注意 publicPath
的正确性。
Base64 编译 |
通过相对路径引入图片的时候,如果图片小于 10K,会被编译为 Base64,否则会被构建为独立的图片文件。
10K 这个阈值可以通过 inlineLimit
配置修改。
4️⃣Umi进阶 |
使用dva models |
我们在创建项目的时候引入了@umijs/preset-react ,这是一个插件集,包含:
- plugin-access,权限管理
- plugin-analytics,统计管理
- plugin-antd,整合 antd UI 组件
- plugin-crossorigin,通常用于 JS 出错统计
- plugin-dva,整合 dva
- plugin-helmet,整合 react-helmet,管理 HTML 文档标签(如标题、描述等)
- plugin-initial-state,初始化数据管理
- plugin-layout,配置启用 ant-design-pro 的布局
- plugin-locale,国际化能力
- plugin-model,基于 hooks 的简易数据流
- plugin-request,基于 umi-request 和 umi-hooks 的请求方案
所以无需再而外安装 plugin-dva ,至于以上插件的用法,看UmiJS Plugins
开启dva |
dva 配置有没有开启,该插件是配置开启的
只需要在配置中开启即可。打开 umi 的配置文件:
//.umirc.js
import { defineConfig } from 'umi';
export default defineConfig({
dva: {},
antd: {}
});
新增 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 文件。
// src/models/hero.ts
import { Effect, Reducer } from 'umi';
export interface HeroModelState {
name: string;
}
export interface HeroModelType {
namespace: 'hero';
state: HeroModelState;
effects: {
query: Effect;
};
reducers: {
save: Reducer<HeroModelState>;
};
}
const HeroModel: HeroModelType = {
namespace: 'hero',
state: {
name: 'hero',
},
effects: {
*query({ payload }, { call, put }) {
},
},
reducers: {
save(state, action) {
return {
...state,
...action.payload,
};
},
},
};
export default HeroModel;
如果文件中的 namespace 未写明,umi 会使用文件名作为 model 的 namespace。为了减少错误的出现,最好保持所有的 model 文件,文件名不同。
在页面中使用model |
// src/pages/hero.tsx
import React, { FC } from 'react';
import styles from './hero.css';
import { connect, HeroModelState, ConnectProps } from 'umi'; ---step1
interface PageProps extends ConnectProps {
hero: HeroModelState;
}
const Hero: FC<PageProps> = (props) => { ---step2
console.log(props.hero); ---step4
return (
<div>
<h1 className={styles.title}>Page hero</h1>
<h2>This is {props.hero.name}</h2>
</div>
);
}
export default connect(({ hero }: { hero: HeroModelState }) => ({ hero }))(Hero);
--- step3
- step1 在文件头部引入了 umi 的connect,HeroModelState, ConnectProps
- step2 把之前的匿名函数,改成实名函数Hero
- step3 使用connect连接页面和models
- step4 从属性中取出namespace为hero的model的state数据。
监听路由事件 |
进入页面的时候,发起请求页面初始化数据。这里我们通过 dva model 的 subscriptions 实现。
src/models/hero.ts subscriptions
import { Effect, Reducer, Subscription } from 'umi';
export interface HeroModelType {
namespace: 'hero';
state: HeroModelState;
effects: {
query: Effect;
};
reducers: {
save: Reducer<HeroModelState>;
};
+ subscriptions: { setup: Subscription };
}
HeroModel 中增加 subscriptions
subscriptions: {
setup({ dispatch, history }) {
return history.listen(({ pathname, query }) => {
if (pathname === '/hero') {
dispatch({
type: 'fetch'
})
}
});
}
},
这里需要注意的是,subscriptions 是一个全局的监听,就是说,当设定触发条件满足时,所有的 subscriptions 都会响应,所以我们在这里判断了路由为当前路由时,发起一个 effects 事件。
proxy 请求代理 |
之所以会出现跨域访问问题,是因为浏览器的安全策略。所以我们预想是不是有一种方式,能够绕过浏览器的安全策略?
那就是先请求一个同源服务器,再由服务器去请求其他的服务器。比如:
- 我们本来是要请求 https://pvp.qq.com 服务器,但是它存在跨域。
- 所以我们先请求了 http://localhost:3000 (假设的),它不存在跨域问题,所以它受理了我们的请求,并且我们可以取得它返回的数据。
- 而由 http://localhost:3000 返回的数据,又是从真实的 https://pvp.qq.com 获取来的,因为服务端不是在浏览器环境,所以就没有浏览器的安全策略问题。
- 因为 http://localhost:3000 (假设的)这个服务器,它只是把我们请求的参数,转发到真实服务端,又把真实服务端下发的数据,转发给我们,所以我们称它为代理。
umi 提供了 proxy 来处理这个问题。
配置 proxy |
export default {
plugins: [
...
],
"proxy": {
"/api": { ---step1
"target": "https://pvp.qq.com", ---step2
"changeOrigin": true, ---step3
"pathRewrite": { "^/api" : "" } ---step4
}
}
}
- step1 设置了需要代理的请求头,比如这里定义了 /api ,当你访问如 /api/abc 这样子的请求,就会触发代理
- step2 设置代理的目标,即真实的服务器地址
- changeOrigin 设置是否跨域请求资源
- pathRewrite 表示是否重写请求地址,比如这里的配置,就是把 /api 替换成空字符
src/app.ts
import { ResponseError } from 'umi-request';
export const request = {
- prefix: 'https://pvp.qq.com',
+ prefix: '/api',
errorHandler: (error: ResponseError) => {
// 集中处理错误
console.log(error);
},
};
=========================================================================
本文简单学习了umi,更深层次学习还需要大家查看官网配置与插件部分。如有不足,还望指出。
参考文档:
umijs官网
yuque umijs