WhatAKitty Daily

A Programmer's Daily Record

以react-script重构antd-pro

WhatAKitty   阅读次数loading...

背景

在蚂蚁金服推出ant-design-pro的开源项目后,仔细了解了下,根据官网的介绍:

开箱即用的中台前端/设计解决方案

不过,该项目是基于dvaroadhog构建,我更倾向于使用react-script作为脚手架,所以也萌生了将现有的ant-design-pro项目的基础构建工具迁移到react-script的想法。

过程描述

1. 使用create-react-app创建一个新的工程

1
2
create-react-app my-project
cd my-project

2. 安装react-app-wiredless-modules

1
yarn add react-app-rewired react-app-rewire-less-modules@https://github.com/WhatAKitty/react-app-rewire-less-modules less-loader --dev

这两个模块的作用主要在于:扩展react-script本身的webpack配置。至于为什么要这么做,是因为为了简化开发流程,react-script本身已经将所有配置都隐藏配置;如果想要自行配置一些插件或者设置babel属性,只能通过react-app-wired来扩展。

3. 修改启动测试脚本

打开根路径下的package.json文件:
将以下内容:

1
2
3
4
5
6
"scripts": {
"start": "react-script start",
"build": "react-script build",
"test": "react-script test --env=jsdom",
"eject": "react-scripts eject"
},

修改为:

1
2
3
4
5
6
"scripts": {
"start": "react-app-rewired start",
"build": "react-app-rewired build",
"test": "react-app-rewired test --env=jsdom",
"eject": "react-scripts eject"
},

4. 创建react-app-wired的配置覆盖文件

新建文件config-overrides.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
const path = require('path');
const { injectBabelPlugin } = require('react-app-rewired');
const rewireLess = require('react-app-rewire-less-modules');

module.exports = function override(config, env) {
// 支持es7的`@`特性
config = injectBabelPlugin('transform-decorators-legacy', config);
config = injectBabelPlugin(['import', { libraryName: 'antd', style: true }], config);

// webpack externals属性
config.externals = {
"g2": "G2",
"g-cloud": "Cloud",
"g2-plugin-slider": "G2.Plugin.slider"
}

// 支持less的css模块化
return rewireLess.withLoaderOptions(
`${env === 'production' ? 'app' : '[local]'}-[hash:base64:8]`,
{
// 覆盖antd的主题属性,具体详细变量请查看:https://github.com/ant-design/ant-design/blob/master/components/style/themes/default.less
modifyVars: {
'@primary-color': '#1DA57A',
'@link-color': '#1DA57A',
'@border-radius-base': '2px',
'@font-size-base': '14px',
'@line-height-base': '1.2',
'@card-actions-background': '#f5f8fa',
}
},
// 将/src/路径包含到css模块化内,默认会将/node_modules/路径排除在css模块化外,具体配置可以查看:https://github.com/WhatAKitty/react-app-rewire-less-modules
new RegExp(`${path.sep}src${path.sep}`),
)(config, env);
};

5. 安装ant-design-pro需要的依赖

以下是ant-design-pro需要的依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
"antd": "3.0.0-beta.5",
"classnames": "^2.2.5",
"dva": "2.1.0-beta.3",
"history": "^4.7.2",
"less": "^2.7.3",
"lodash": "^4.17.4",
"lodash-decorators": "^4.5.0",
"lodash.clonedeep": "^4.5.0",
"moment": "^2.19.1",
"numeral": "^2.0.6",
"postcss-less": "^1.1.1",
"qs": "^6.5.1",
"react-container-query": "^0.9.1",
"react-document-title": "^2.0.3",
"react-fittext": "^1.0.0",

通过以下命令安装:

1
yarn add xxxx

注意,有些事prerelease版本,需要指定特定版本安装,比如:antddva

使用beta版本的原因如下:
atndant-design-pro以3.x为基础构建
dva: dva2.1.0-beta版本修复了一个import的issue,如果使用2.0.4版本,我们的项目将无法启动。

6. 迁移ant-design-pro代码

到了比较重要的一个步骤了,这个步骤我们需要将ant-design-pro的代码迁移至我们的项目。

在这里,我们分为几个小步骤进行:

  • 删除我们自己项目的src文件夹下所有内容
  • src文件夹下内容迁移至我们项目的src文件夹下
  • 修改/public/index.html文件,将g2图标组件静态导入:

    1
    <script src="https://gw.alipayobjects.com/as/g/??datavis/g2/2.3.12/index.js,datavis/g-cloud/1.0.2/index.js,datavis/g2-plugin-slider/1.2.1/slider.js"></script>
  • 安装react-fetch-mock替代rodhogmock

    1
    yarn add react-fetch-mock --dev

创建/src/__mocks__文件夹,并且将ant-design-pro根路径下mock文件夹内容全部迁移至/src/__mocks__文件夹下。

/src/__mocks__文件夹下新建index.js文件:
复制.rodhog.mock.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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import { Mock } from 'react-fetch-mock';
import { getRule, postRule } from './rule';
import { getActivities, getNotice, getFakeList } from './api';
import { getFakeChartData } from './chart';
import { imgMap } from './utils';
import { getProfileBasicData } from './profile';
import { getProfileAdvancedData } from './profile';
import { getNotices } from './notices';

export default {
// 支持值为 Object 和 Array
'GET /api/currentUser': () => {
return {
name: 'Serati Ma',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/dRFVcIqZOYPcSNrlJsqQ.png',
userid: '00000001',
notifyCount: 12,
}
},
// GET POST 可省略
'GET /api/users': () => {
return [{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
}, {
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
}, {
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
}]
},
'GET /api/project/notice': () => getNotice,
'GET /api/activities': () => getActivities,
'GET /api/rule': getRule,
'POST /api/rule': (obj) => {
return postRule({
...obj, urlparams: {
pageSize: {
desc: '分页',
exp: 2,
},
}
});
},
'POST /api/forms': () => {
return {};
},
'GET /api/tags': () => {
return Mock.mock({
'list|100': [{ name: '@city', 'value|1-100': 150, 'type|0-2': 1 }]
}).list;
},
'GET /api/fake_list': getFakeList,
'GET /api/fake_chart_data': () => getFakeChartData,
'GET /api/profile/basic': () => getProfileBasicData,
'GET /api/profile/advanced': () => getProfileAdvancedData,
'POST /api/login/account': ({ params }) => {
const { password, userName } = params;
return {
status: password === '888888' && userName === 'admin' ? 200 : 401,
type: 'account',
};
},
'POST /api/login/mobile': () => {
return {
status: 200,
type: 'mobile'
};
},
'POST /api/register': () => {
return {};
},
'GET /api/notices': getNotices,
}

同时,__mocks__文件夹下其他非index.js文件,需要根据react-fetch-mock规则进行修改。具体用法,可以参考:https://github.com/WhatAKitty/react-fetch-mock。改造后的文件内容,可以在此查看:https://github.com/WhatAKitty/react-script-antd-pro/tree/master/src/__mocks__

7. 完成

改造基本完成,可以通过下面命令来查看具体效果:

1
yarn start

结束语

通过以上,我们已经完成改造。
如果嫌自己改造麻烦,这里是已经完成的版本:https://github.com/WhatAKitty/react-script-antd-pro,并且会根据`ant-design-pro`进行每天更新。

扩展

有空的话,会将动态路由的改造过程描述加入。现在也可以通过上面那个完成版本里面查看动态路由相关信息,在那个版本中,动态路由已经加入。