基于 Umi 的最佳实践

  • 本项目使用 yarn 进行包管理
  • 全局安装的命令为 jsdoc

本项目是基于 Web 开发最佳实践 的技术扩展

目录结构以及解释

目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
├── build           # 打包目录
├── config # 配置目录
│ ├── config.dev.js
│ ├── config.js
│ └── ...
├── docs # 文档目录
│ ├── html
│ └── jsdoc.json
├── jsconfig.json # IDE 的代码提示 @/
├── package.json # 版本以及依赖文件
├── public # 附加到 public 目录 的文件
│ └── robots.txt
└── src
├── assets # 资源文件
│ ├── images # 图片文件
│ └── less # 样式文件
│ ├── style.less # 样式主文件
│ └── ...
├── build.md # 版本定义说明
├── layout # 布局文件夹
│ └── index.js
├── models # 模型文件, 用户全局的数据管理
│ └── poppy.js
├── pages # 所有页面
├── services # api 服务定义页面
│ └── poppy.js
└── utils # util 包, 多个项目内容应该一致
├── conf.js # 配置
├── request.js # 基于 axios 的请求
├── routes.js # 路由定义
├── ui # UI 部件
│ └── IconFont.js # IconFont 字体
└── util.js # 辅助功能

配置文件

config.test.js

1
2
3
4
5
6
7
8
9
import { defineConfig } from "umi";
export default defineConfig({
// 打包路径
outputPath: "build/proj-test",
define: {
// 定义的URL被覆盖
"process.env.API_URL": "https://t.proj.domain.com",
},
});

配置中使用的变量说明, 其他变量间 umi 官方文档
config.js

1
2
3
4
5
6
7
8
9
10
11
export default defineConfig({
// ...
define: {
// 版本号, 取 package.json 中的版本
"process.env.VERSION": version,
// 接口请求主地址, 尾部不包含斜线
"process.env.API_URL": process.env.API_URL || "",
// 打包环境, 用户 sentry 提交错误对应正确的环境
"process.env.UMI_ENV": process.env.UMI_ENV || "",
},
});

打包以及运行说明

命令放置在 package.json 文件中

1
2
3
4
# 运行本地环境(参考package.json启动其他环境)
$ yarn start:test
# 构建测试环境(参考package.json构建其他环境)
$ yarn build:dev
  • UMI_ENV : 代表的是运行环境, 可以在配置中取到相应的文件并进行相应的处理
    1
    2
    3
    4
    5
    6
    7
    8
    9
    {
    "..."
    "scripts": {
    "...",
    "start:test": "cross-env UMI_ENV=test yarn umi-dev",
    "build:test": "cross-env UMI_ENV=test umi build",
    "..."
    }
    }
    打包出来的路径为 build/proj-test

文档

文档使用的 jsdoc. 这里的配置文件使用的是 ink-docstrap  模板, 配置如下
使用如下命令生成

1
$ jsdoc -c docs/jsdoc.json

jsdoc.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"tags": {
"allowUnknownTags": true,
"dictionaries": ["jsdoc", "closure"]
},
"source": {
"include": ["./src/", "README.md"],
"exclude": [],
"includePattern": ".+\\.js(doc|x)?$",
"excludePattern": "(^|\\/|\\\\)_"
},
"plugins": [],
"opts": {
"template": "./node_modules/ink-docstrap/template",
"encoding": "utf8",
"destination": "./docs/html",
"recurse": true
}
}

版本定义

项目版本放在 version 中, 每次上线或者切换分支的时候都需要更新, 便于后端进行统计
package.json

1
2
3
4
5
{
"name": "proj-mobile",
"version": "1.1.0",
"..."
}

服务的定义以及使用

使用 api 作为前缀, 便于区分
src/services/poppy.js

1
2
3
4
5
6
7
8
9
10
/**
* 国别码
*/
export function apiPamLogin(params) {
return request({
url: "/api_v1/system/pam/login",
method: "post",
data: params,
});
}

使用

1
2
3
4
5
6
apiPamLogin({ passport: country + "-" + mobile, captcha: captcha }).then((resp, status, message, data) => {
// resp : 包含完整的请求结果
// status : 错误码信息
// data : data 中的数据
// message : 返回的信息
});

模型的定义以及说明

模型定义

src/models/poppy.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import { Toast } from "antd-mobile";
import { apiAreaCountry } from "@/services/poppy";
import { storageKey } from "@/utils/conf";
import { sessionStore } from "@/utils/util";
const poppyModel = {
namespace: "poppy",
state: {
// 1. 初始数据值
countryCode: [],
},
effects: {
// 2. 定义的请求
*reqAreaCountry({ payload: params }, { call, put }) {
// 3. 获取数据
let storeData = sessionStore(storageKey.PY_AREA_COUNTRY);
if (!storeData) {
// apiAreaCountry 在 模型中的使用, 参数可以在第二个参数传入
const { data, success, message: msg } = yield call(apiAreaCountry, {});
if (success && data) {
sessionStore(storageKey.PY_AREA_COUNTRY, data);
storeData = data;
} else {
Toast.fail(msg);
}
}
// 4. 更新数据
yield put({
type: "UPDATE_COUNTRY_CODE",
payload: {
countryCode: storeData,
},
});
},
},
reducers: {
// 5. 将数据写入 redux store 中
UPDATE_COUNTRY_CODE: (state, { payload }) => {
return {
...state,
...{
countryCode: payload.countryCode,
},
};
},
},
};
export default poppyModel;

数据的调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import React, { Component } from 'react';
import $ from 'jquery';
import { CloseCircleOutlined } from '@ant-design/icons'
import BScroll from 'better-scroll';
import { get } from 'lodash-es';
import { connect } from 'umi';
// 1. 连接 store
@connect(({ poppy }) => {
return {
countryCode: poppy.countryCode
}
})
class CountryCode extends Component
{
scroll = '';
touch = {};
constructor(props) {
super(props);
this.state = {
list: {},
alphaList: []
}
}
componentDidMount() {
// 2. 触发数据更新
// 这里的 dispatch 必须使用 store 之后才可以触发, 毕竟读取的是store 里边的数据
this.props.dispatch({
type: 'poppy/reqAreaCountry'
})
}
// 3. 接收数据更新
static getDerivedStateFromProps(nextProps, prevState) {
const countryCode = get(nextProps, 'countryCode');
const alphaList = get(prevState, 'alphaList');
if (countryCode && alphaList.length === 0) {
return CountryCode.initList(countryCode)
}
return null;
}
static initList(countryCode) {
let alpha_li = [];
let list_li = {};
countryCode.length > 0 && countryCode.forEach((item) => {
let key = item['py'];
alpha_li.push(key);
list_li[key] = list_li[key] ? list_li[key].concat(item) : [item];
})
return {
alphaList: [...new Set(alpha_li)],
list: list_li
}
}
render() {
const { list, alphaList } = this.state;
return (
// 数据渲染
);
}
}
export default CountryCode;

语雀镜像 : 基于 Umi 的最佳实践 ,点此 提问

作者

Duoli

发布于

2022-04-20

更新于

2022-09-30

许可协议

评论