终于,入职20天了。今天吃了烤串庆祝!
最近一周使用react框架做了一个具有简单功能的异步监控平台,是接触react之后做的第一个项目,简单总结一下。
一、开发需求
一个异步监控平台,需要显示服务器的名称、进程名称以及进行一系列操作,包含:开始、停止、重启、显示日志等,没有设计图,给出的参考图如下:
而这个项目又有些特殊的要求,需要控制的项目也更多,需要有:机器、组、进程这三个概念,因此,最终设计成了如下形式:
如图,这是一台机器中的三个进程,分为两个进程组,每个组中对应着不同的进程,而这些进程都需要进行相应的操作。使用react开发,可以将这些内容分为组件,降低开发难度。
二、开发环境
本项目直接复用的组内其他项目的结构。主要基于通过对本部门业务项目的需要,在fis和webpack的基础上集成了一套打包方式(内部称eden)。部分依赖如下,供参考:
"babel-core": "^6.5.2",
"babel-loader": "^6.2.3",
"babel-preset-stage-0": "^6.5.0",
"css-loader": "^0.26.1",
"eden-remote-deploy": "^0.0.1", //eden版本
"express": "^4.15.2",
... ...
"q": "^1.5.0",
"react-transform-hmr": "^1.0.4",
"serve-static": "1.12.2",
"style-loader": "^0.13.1",
"url-loader": "^0.5.8",
"webpack": "2.4.1",
"webpack-dev-middleware": "1.10.2",
"webpack-hot-middleware": "2.18.0",
"webpack-merge": "^4.1.0"
三、具体实现
1. react页面结构搭建
如开发需求中提到的,组件化开发可以降低开发的难度,但同时,在项目的开始如何分配组件就显得至关重要了。
根据项目的需求,我一开始将这个app分成了四个组件,分别是:
Header:图中的黑色条,项目的抬头,暂时用来显示标题,日后可以拓展到功能键的显示(扩展了一个登入-登出的功能);
Server:图中的深灰色框框,用来显示每一台机器的容器;
Title:图中的浅灰色条条中的三个按钮,用来控制一个进程组的动作;
Structure:图中的表格部分,用来显示每一组进程的容器
在确定了相关组件之后,我开始进行组件开发,之所以考虑将Title组件单独拿出来,是为了尽量简化Structure这个组件,因为,如果不这样做,Structure组件需要同时控制组进程和单个进程的操作,逻辑比较复杂;但是实际开发过程中,发现如果将Title组件独立出去,会导致更多的麻烦,主要出现在数据传输上,即,数据需要从Server传入Structure中,再传入title中,因此,最终删除了title组件。最终保留了三个组件:
在确定好这些之后,就可以开始页面的搭建了。
在每个组件中填入需要渲染的元素即可。本项目的主体是使用表格实现的。
2. UI库使用
第一次开发,使用了公司一直在用的antd库,主要使用的组件有:Button, Table, Icon, Tooltip, Tag, Modal, Row, Col等。
在使用antd库的时候,有很多注意事项,这在我的另一篇文章中有提到,这里不做详述了。
本项目的主体是使用antd中的Table组件搭起来的。该组件有两个主要的属性:columns&&dataSource。columns用来设置每一列的值,dataSource则用来选择数据源。最终使用<Table />进行生成。当然,该组件也有很多其他属性,本项目做的配置如下:
其他组件在另一篇文章中也有所提及,因此不做详述;值得一提的是,在使用Modal组件时,发现其中的文字无法选择,后来发现是一个css3的属性:user-select设置成了none, 因此,在antd.min.css文件中进行了定制化主题,将所有的user-select属性值都改为了text(使用正则表达式
(?<=user-select:)none
进行向前匹配并进行替换,效率更高哦)。附:user-select的属性值:
auto——默认值,用户可以选中元素中的内容
text——用户可以选择元素中的文本
element——文本可选,但仅限元素的边界内(只有IE和FF支持)
all——在编辑器内,如果双击或上下文点击发生在子元素上,该值的最高级祖先元素将被选中。
在所有的组件插入后,最重要的数据传输步骤来到了!前方高能。。。
3. 交互实现
该项目是一个异步管理项目,主要需要进行的数据交互是:点击按钮后发出不同的数据请求,从后端拉回新的返回数据,同时更新UI界面。
要实现这样的异步传输系统,分为几个步骤:
1)点击按钮的时候触发事件,同时传递参数;
2)向指定接口传递数据;
3)从指定接口获取最新数据,更新UI页面;
这样,就完成了一个数据传输的过程。
再看具体实现。
首先是第一步,具体实现方法如下:
<button onClick={ () => this.props.startAll({
server_name:name})}
className='actionButton'
>...</button>
在一个button中绑定startAll方法,并传入server_name参数;
接下来,是通过startAll方法向指定接口传参,这里使用的是react-refetch
。使用react-refetch
,首先需要引入,这里主要是引入connect:
import { connect, PromiseState } from 'react-refetch'
接着就是在项目中触发,本项目中选择的触发方式:
export default connect (props=>({
startAll: params=>({
startAllResponse: {
url:/smonitor/api/***,
method:'POST',
body: formurlencoded(params),
force:true,
headers: {
'Content-Type':'application/x-www-form-urlencoded'
}
}
})
//通过这样的操作,将请求参数发出;
最后,从指定接口获取数据并进行UI界面更新:
要完成此操作,首先应该通过react的生命周期componentWillReceiveProps
来完成,通过向此周期中传入nextProps
参数,来检测当整个app的props改变之后,项目需要进行的操作。在此项目中,这一步需要显示从后端拉过来新的服务器状态,因此,需要仿照第二步的做法,新建一个refresh方法,并且重新向服务器请求参数:
refreshList:params => ({
refreshAllResponse: {
url: `/smonitor/api/***`,
method: 'POST',
body: formurlencoded(params),
force: true,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}
}
)
之后,在componentWillReceiveProps
生命周期中引入这个方法,来刷新整个页面的显示:
componentWillReceiveProps(nextProps) {
if (nextProps.startAllResponse && !nextProps.startAllResponse.pending && this.props.startAllResponse.pending) {
const responseValue = nextProps.startAllResponse.value;
if (responseValue.errno == 0) {
this.props.refreshList({
server_name:this.props.name
})
}
}
通过这样的方式,就完成了页面的刷新显示。
4. 状态管理引入
这一步主要做的是引入redux管理机制。目前还没做,只提出设想。
首先是将所有的动作(包含点击按钮后的行为)集成到action中,之后在reducer中写入生命周期中的内容,最后通过connect将recucer将module和action相连接。
具体的实现了再补充过来。。。
四、本地数据测试
在整体的逻辑走通之后,接下来就是使用本地的mock数据进行调试。具体的格式要与服务器请求的相同。
{
"homepage": {
"logout_url": "http:***,
"user_info": {
"uid": "344",
"uname": "xuran",
"email": "xuran@iwaimai.baidu.com"
},
"local-server": {
"version": "3.3.1",
"process_list": [
{
"group": "apilog01",
"name": "apilog001",
"description": "pid 2813, uptime 0:00:02",
"statename": "STOPPED",
"state": 0
}
]
},
"error-server": {
"version": "3.3.1",
"process_list": [
{
"group": "apilog02",
"name": "apilog001",
"description": "pid 2813, uptime 0:00:02",
"statename": "EXITED",
"state": 100
}
]
}
}
}
本地数据测试无误后,可以推代码到后端开发机上进行测试。
五、坑。。。
- mock数据的配置问题:之前启动项目时,mock数据一直显示404,经查找之后发现,是mock数据路径没有被拦截成功的原因,这在eden的项目配置过程中需要重点注意;
- 注意上线环境与本机环境的区别。两个大坑:第一个是使用的iconfont字体的跨域问题,阿里cdn会拦截本厂域名,因此通过下载到本地并改变webpack配置来解决这一点;第二个是要将所有本地内容转换为线上内容,比如登出链接,就需要从模板中取。。。
- 处理数据时要考虑到数据结构可能进行扩充,因此需要进行多种考虑。本项目中,
homepage
下原先只有local-server
这一个对象,后来又扩展了logout_url
和user_info
这两个字段,因此之前的数据读取方式不能再用,这是一开始开发时没有考虑到的情况。
这样,算是跌跌撞撞完成了第一个项目的开发和上线。日后的优化方向:引入redux状态管理机制&&完善Eden。
未完待续。。