开发者学堂课程【基于STM32的端到端物联网全栈开发:前端服务开发(2)】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/574/detail/7950
前端服务开发(2)
内容介绍:
一、初始化前端项目
二、使用 Layout 组件
三、主页面
四、卡片组件的使用
五、网络请求
六、dva 管理数据
七、dva 数据流转项目下载
八、项目打包
九、项目部署
十、总结
十一、课程场景概览
内容简介:
本节将开始初始化前端项目,组件的使用和数据流转,部署。本小节将介绍前端项目的初始化,主要以操作为主,使用 umi 提供脚手架进行。
一、初始化前端项目
使用 umi 脚手架初始化搭建的项目页面如图所示。
1、软件环境安装
在正式开始之前,需要系统已经安装以下软件环境:
(1)Node.js
Node.js 是一个高性能的 JavaScript 运行环境,基于强大的 Chrome V8引擎打造,
安装地址:https://nodejs.org/zh-cn/
(2)yarn
yarn 是流行的包管理工具,Yarn 会缓存它下载的每个包,所以无需重复下载。它还能并行化操作以最大化资源利用率,安装速度之快前所未有。安装地址:
https://yarnpkg.com/zh-Hans/docs/install#windows-stable或: npm install yarn tyarn -g
(3)umi
Umi 是一款基于路由的前端开发框架,目前为蚂蚁金服底层前端框架。
安装方式:命令行执行:yarn global add umi
2、新建项目文件夹
(1)Linux&macOS
在保存项目的文件夹下采用 terminal 执行:mkdir frontendproj、cd frontendproj。
(2)Windows
在保存项目的文件夹下右键新建文件夹 frontendproj,进入文件夹,在文件通过 powershell 进入 shell 终端。
3、在项目文件夹内命令行执行: yarn create umi
脚手架工具将会自动安装必备的软件包依赖,通过按钮选择引用类型为 app->不使用 TypeScript (n) -> 选中 antd 和dva 功能块,选择结束后,umi 将通过脚手架生成项目模版。
4、在项目文件夹内命令行执行: yarn 安装依赖
yarn 命令会依照 package.json 中定义的依赖包内容自动安装依赖到文件夹下 node_ modules 子文件夹。
mock/ // mock 文件所在目录,基于 express
node_ modules/ //依赖包安装文件夹
src/ //源码目录,可选
Tayouts //全局布局 开发中 主要编辑的文件
项目初始化目录结构
Models // dva models 文件存放目录,存放 global 的 models
pages/ //页面目录,里面的文件即路由
.umi/ // dev 临时目录,需添加到.gitignore
index.js // HTML 模板
index.css // 404页面
global.css //约定的全局样式文件,自动引入,也可以用 global. less
app.js //可以在这里加入 polyfill
. umirc.js // umi 配置,同 config/config.js,二选一
. Env //环境变量
package.json
5、在项目文件夹内命令行执行: yarn start 启动项目
webpack 会自动编译当前项目文件,并通过本地网络地址8000端口开放访问,访问到以下页面即通过脚手架工具初始化成功。脚手架,可以理解为‘“项目模版”会帮助搭建一个完整的前端应用。
访问 localhost:8000
6、修改组件内容和 Layout
pages/index.js 文件即为首页小丑页面组件,layout/index.js 即为页面布局(layout) 组件,分别修改其中内容观察组件内容变化。
<Layout>
{this.props.children}
</Layout>
umi 将 Layout 组件嵌套在 content 上,访问 content 下路由 Layout+content
Layout 组件通过 this.props.数据属性获取子组件的内容。当用户访问 content 的页面下的路由时,umi 会将 Layout组件嵌套在 content 上,并在浏览器中呈现出来。
7、添加新的页面
Umi 采用约定式路由,即会通过 pages 下新增的文件名或文件夹名称创建组件的路由,在 pages 下新建 test.js 或test/index.js,写入组件内容后保存,修改浏览器路由即可访问新的组件内容。
import React from 'react' ;
export default class Test extends React. Component {
render() {
return (
<div>
<h1>Test</h1>
</div>
);
}
}
浏览器访问 localhost:8000/test
接下来的内容将对 layout 组件进行改善,对主页页面进行拆解和组件引入,使脚手架的初始化项目更加贴近项目原型。
二、使用 Layout 组件
1、Layout 组件
Ant Design 提供了丰富的 layout 组件以及示例代码参考: https://ant.design/components/layout-cn/,可以通过 Ant Design 官网轻松获取组件的使用说明以及示例代码。
2、Layout 组件引入
修改项目 layout/index.js 组件内容,引入 Antd 提供的 layout 组件内容,保存后新的 layout 内容会自动替换。
例如引入 Antd 提供的 layout 页面,将页面分为 header 导航栏、content 内容区以及 footer 底栏,通过及其结论和代码即可轻松实现。代码如下:
import React from 'react';
import " antd/dist/antd.css';
Import {Layout }from " antd';
const {Header, Footer, Content }= Layout;
class BasicLayout extends React. Component {
render() {
return (
<div>
<Layout>
<Header >
header
</Header>
<Content>
{this , props children}
</Content>
<Footer style={{textAtign:
‘
center
’
, backgroundcolor: #001529 }}>
<div style={{ color: "white" }}>
footer
</div>
</Footer>
</Layout>
</div>
);
}
}
export default BasicLayout ;
3、完善组件内容
根据设计稿,需要在 header 添加 logo 内容以及设备选择下拉框,前端需要通过用户选择的 devicename 参数向后端进行数据请求,下拉框使用案例上提供的 select 组件,修改 BasicLayout 内容为下:
import React from 'react' ;
import " antd/dist/antd.css' ;
import { Layout, Select} from
‘
antd
’
;
const Option =Select .Option;
const { Header, Footer, Content }= Layout ;
class BasicLayout extends React .Component {
render() {
//Select 组件的选择回调函数
const onSelect =(value) => {
console.log("
选中
: " + value);
}
Return(
<div>
<Layout>
<Header style={{display:
’
faflex', justifyContent: " space-between' }}>
<div style={{ color: "white", fontSize: '24px', fontWeight: "bold', float: 'left' }}
>阿里云 Iot | ST 只能家具教程 </div>
<div style={{ display: 'flex', justifyContent: " space- between', alignItems: 'center', color: 'white' }}>
请选择设备
devicename
:
<Select style={{ width: '200px', float: 'right' }}
Placeholder=*
当前返回无设备 "//当默认没有 Option 或者为选中 option (注释 defaultValue)时显示
onChange=(onSelect )//select 组件的选择回调,输出选中 option 的 value 属性,参见 select 组件 API 文档
defaultValue={"默认设备[待替换]") //默认选中的选项,应填充设备
这部分是使用 Antd Select 下拉框组件
<0ption value={" devicel"}>device1</0ption>
<option value={"device2" }>device2</Option>
<0ption value={"device3*}>device3</Option>
</select>
</div>
</Header>
<Content>
{this . props . children}
</Content>
layout
Content
<Footer style={{ textAlign:
‘
center' , backqroundColor:
’
#001529' }}>
<div style={{ color: "white"}}>
Powered By abits .cn
</div>
</Footer>
</Layout>
</div>
};
}
export detault BasicLayout ;
4、完善组件内容
保存后,layou 更新的内容有 layout 的 content,使用 Antd Select 下拉框组件。
5、待完善的内容
至此完成了 header 内容的完善,但是还需要完善的如下:
(1)组件联动
在 Header 中加入设备选择后,BasicLayout 的作用不仅仅是给页面提供样式嵌套,还需要通过 deviceName 的选择动态更新 Content 中包裹的设备数据的相应变化,实现多个设备数据在页面上有序的切换。
(2)数据请求
在 Select 组件的 defaultValue={“默认设备[待替换]"}属性中,组件应提前向后端服务请求设备的列表,将列表的第一个设备 deviceName 填充进 defaultValue 属性,当 Select 选择新的 deviceName 时,需要完成新的 deviceName下的设备数据请求,替换已有下拉框内容。组件的数据部分介绍将在下一节 dva 数据流内容进行讲解。
三、主页面
1、页面拆解
根据设计稿,在主页面中需要展示设备当前状态和设备历史状态(历史温湿度曲线、温度报警表格)两个块,为了使得项目代码清晰易懂可维护,使用 React 的组件化实现复杂页面的组件化拆解,拆解出来的组件作为一个独立文件,通过组件组合出目标页面。通过拆解页面可以更大程度上使得各个组件结构将复杂的页面内容以搭积木的方式组合起来,是常用的前端工程实践方法。
card 组件
在拆解后,在主页面需要确定各子组件的布局及位置,各组件位置可以通过字符进行占位。使用 Ant Design[Card 组件]快速实现主页面的框架。[Card 组件]提供内容块隔离的样式,可通过配置 Card.Grid 快速实现,免去写复杂的 CSS样式。[Card 组件]同时提供可切换的 Tab 页,通过 TabList AP| 即可快速实现 Tab 页的切换,同时也和 Card.Grid 保持风格上的一致。
根据拆解划分的组建内容,修改项目的目录如下:
mock/
// mock 文件所在目录,基于 express
node_ modules/
//依赖包安装文件夹
src/
//源码目录,可选
开发中主要编辑的文件
layouts
//全局布局
models
// dva models 文件存放目录,存放 globaL 的 models
pages/
//页面目录,里面的文件即路由
.umi/
// dev 临时目录,需添加到.gitignore
主页拆解 UI 组件
main/
//主页组件目录
index.js
// main 主页组件
component/
//存放子组件的目录
四张卡片组件
Card/
//存放设备实时状态的四个卡片
AlarmStatus.js
// 报警状态
HumiStatus.js
//湿度状态
TempStatus.js
//温度状态
OnlineStatus.js //在线状态
曲线图和报警表格组件
Chart.js
//曲线图组件
Table.js
// 表格组件
index.js
// umi默认欢迎页
index.css
//欢迎页 css 文件
global.css
//约定的全局样式文件,自动引入,也可以用 global. less
app.js
//可以在这里加入 polyfill
.umirc.js
// umi配置,同 config/config.js, 二选一
.env
//环境变量
package.json
2、框架搭建
(1)同时修改 main 文件夹下的/index.js 文件内容为以下:其中1位4张卡片的内容由 card.grid 进行布局,二为曲线图以及报警表格的组件使用 const tablist 的 API 进行布局。
import React from 'react'
import ' antd/dist/antd.css';
import { Card, Icon, Tooltip } from 'antd';
export default class Main extends React . Component{
constructor(props) {
super(props);
//记录当前选中的 Tab
this.state = {
key: 'tab1',
}
}
//Tab 之祠切換
onTabChange = (key, type) => {
this.setState({ ltype]: key });
}
render() {
//显示设备历史状态的 TabList
曲线图和报警表格组件
const tabList = {
key: 'tab1',
tab: (
<Tooltip title="
设置历史温湿度数据查询">
设备历史状态
</Tooltip>
}
,
{
key: 'tab2',
tab:
<Tooltip title="
设置历史温湿度数据查询">
设备历史状态
</Tooltip>
),
const contentList ={
tab1: (
<div>
这里是历史温湿度曲线图组件
</div>),
tab2: (
<div>
这里是温湿度历史报警组件
</div>),
};
//第一 Row 的 Style 内容
const gridStyle = {
width: '25%'
,
height: ' 200px'
,
textAlign: ' center',
padding: ' 10px'
};
return (
<div style={{backgroundRepeat: 'no-repeat'
,
backgroundSize:'100% 100%'}}>
<div style={{ padding: '38px
°
}}>
<Card
title={
<div>
<Tooltip title="
显示设备当前的运行状态">
设备当前状态
</Tooltip>
</div>}
extra={
<div>
<Icon type="reload" style={{ paddingRight: '5px' }} />
<Tooltip style={{ fontSize: '12px'
》
} title=
"设备最新一次上报属性的时间">
最新一次上报时间:这里待填充设备最新一次上报时间
</Tooltip>
</div>
}
style={{ width: '100%
’
}
>
四张卡片组件
<Card.Grid style-(gridStyle)>
<div>这里是在现状态卡片< </div>
</Card. Grid>
<Card.Grid style={gridStyle}>
<div>这里是报警状态卡片</div>
</Card. Grid>
<Card.Grid style={gridStyle}>
<div>这里是温度卡片</div>
</Card.Grid>
<Card.Grid style={gridStyle}>
<div>这里是温度卡片</div>
</Card. Grid>
</Card>
</div>
<div style={{padding: '30px', paddingTop: 0 }}>
<Card
style={{ width: '100%' }}
tablist={tabList}
activeTabKey={this. state. Key}
onTabChange={(key) => {this. onTabChange(key, 'key'); }}
>
{contentList [this.state. key]}
</Card>
</div>
</div>
)
}
}
保存修改 main/index.js 后,访问浏览器 localhost:8000/main,页面输出,至此以初步搭建好主页的页面框架。
(2)在 main 组件中,使用了 Ant Design 提供的两个组件,[Icon 图标] 和 [Tooltip 文字提示]。
Tooltip 组件实现简单的文字提示起泡框,通过<Tooltip>组件包裹目标组件即可,通过 Tooltip 的 titleAPI 进行提示文字设定。
Icon 是 Ant Desgin 提供的语义化矢量图形组件,可以通过配置 Icon 的 Type 属性快速集成 AntDesign 提供的图标库中的图标。
首页在 Card 组件中,使用卡片组件搭建起了页面的基本骨架,要实现此组件之间的解耦,还需要将子组件引入进来,例如修改 OnlineStatusCard 组件内容为以下,并插入到 main/index.js 主页面的 Card.Grid 位置。同理引入其它组件。
项目参考代码:仓库/exercis1, 执行 yarn 安装依赖
四、卡片组件的使用
1、card 卡片组件
(1)Onlinestatuscard
组件需要使用请求到的设备在线状态数据,显示设备不同的在线状态。
OnlineStatusCard 数据
在线状态:state
最近一次上线时间: recentOnlineTime
由于目前还没有使用 dva,先用组件内的假数据进行 online 展示。
OnlineStatusCard 样式
通过 Antd 提供 Icon,判断在线状态,显示不同的图标。美观度主要依靠 CsS 功底。在橙色内通过判断在线状态与否,分别渲染组件的内容,将对组件的在离线状态进行判断,并展示不同图标样式,意味着美观度需要具备一定的css功底。
(2)AlarmStatusCard
报警组件需要显示设备的温度报警状态,以及最近报警时间,在报警后,需要点击图标下发报警解除(向后端进行请求)。
OnlineStatusCard 数据,报警状态: state,最近报警时间: recentAlarmTime,采用虚假的数据进行暂时的表示。
报警组件的完整内容如下,针对设备报警与否分别展示不同的内容,具体表现在设备发生报警后 icon 为叹号并进行旋转,如设备出现报警,则按 onclick 的方法可以进行调用。用户可以点击图标清除报警,前端通过 clearalarm API 调用后端下发消息给设备。
import React from " react'
import
‘
antd/dist/antd.css';
import {Icon, Divider, Tooltip} from 'antd';
const alarmcard ={
state:false,
recentAlarmTime: '2019-01-0100:00:01 ‘
}
export default class AlarmStatusCard extends React.Component{
render() {
//清除警报点击事件回调
const clearAlarm = () => console. log
("点击了清除警报");
return (
<div style={{display: 'flex', alignItems: 'center', paddingTop: '5%', justifyCont
<div style={{
position: " relative',
border: 'lpx solid #e8e8e8’ ,
borderRadius: "4px' ,
height: "100%", width: "100%",
}}>
<div style={{
position:" absolute', top: -20, left: 40,
height: '100px', width: " 100px' ,
backgroundColor: " #e25858' ,
borderRadius: '4px',
boxShadow: '0 0 10px grey',
display: 'flex', alignItems: 'center', justifyContent: " center' ,
}}
alarmCard.state ===true ? (
//发生报警
<Tooltip title=
"点击按钮即可可清除警报*>
<Icon
onCl ick={elearAlarm}
//点击后的回调函数
Spin={true}
//转动
type={"excl amation-circle"}
Style={{ color: 'white', fontSize: '60px' }}
/>
</Tooltip>
)
:(
<Icon
Type= {"check-cirele"}
Style={{color: 'white', fontsize: '60px'}}
/>
)
}
</div>
<div style={{ textAlign: ' right', paddingRight: " 50px" }}>
<div style={{ fontSize: '16px', marginTop: "0px' }}>
设备报警状态 </div>
anbsp;
<div style={{fontSize: '30px', fontWeight: 'bold' }}>{alarmCard.state===true ?
*报警" : "正常*}</div>
</div>
<Divider
Style={{
marginTop: 0
marginBottom: 0,
fontsize: "12px",
padding: '10px Spx 0 Spx'
}}
>最近报警时间
</Divider>
<div style={{ fontsize: " 12px" }}>{alarmCard. recentAlarmTime ? alarmCard. recentAlarmTime . "
暂未报警
" )</div>
</div>
</div>
);
}
}
最终确认出来的设备正常以及报警状态,组件样式,温度卡片以及深度卡片的内容则较为简单,请参考示例提供的代码。正常:
<icon
type={ " check-circle"}
style={{ color:'white', fontSize: ' 60px' }}
/>
报警:
<Tooltip title="
点告按鈕即可清除警扱">
<Icon
onClick={clearAlarm}
//点击后的回调函数
spin={true}
//转动)
type={"exc' lamation-circle"}
style={{ color: 'white', fontSize: ' 60px' }}
/>
</Tooltip>
2、温湿度卡片
温湿度卡片较为简单,请参考课程提供的源码。
README . md
mock
package. json
src
assets
yay.jpg
axios
index.js
tools.js
global.css
layouts
index.css
index.js
pages
index. CSS
index.js
main
components
Card
AlarmStatus. js
Humistatus.jis
onlinestatus.1 s
TempStatus.is
Chart
index.js
Table
index.js
index.js
mode' ls
main.js
tslint . yml
webpack. config.js
yarn-error. log
yarn. Lock
3、BizCharts 曲线图组件
(1)HistoricalChart
HistoricalChart 实现历史温湿度数据的展示,通过起止时间请求特定时间段的数据,此外还需要在表格绘制温度报警界限,同时通过输入框提交温度报警阈值。
(2)BizCharts
BizCharts 是阿里巴巴提供的开源的图表类组件库,是基于 AntV 的「G2」在 React 下的实现,支持丰富的数据图形化表达。温湿度曲线,使用 bizchart 提供的「折线图」绘制,温湿度各绘制一条,采用双坐标轴。
由于 umi 默认没有集成 Bizcharts,可以通过yarn 或 npm 命令在项目内安装:yarn add bizcharts 或 npm install --save bizcharts
(3)DatePicker
「DatePicker 日期选择框」组件是 Antd 中用于选择时间,内部包含 DatePicker、WeekPicker、MonthPicker 等子类,「RangerPicker」是 DatePicker 中选择时间段的组件,通过 API 中 onOk()提供的回调函数,可以获得点击组件弹窗确认(ok)按钮后的起止日期参数:
RangerPicker 可以通过 defaultValue 属性设定默认值,通过 ranges 属性可以快速选择时间段,为了方便生成各类时间,我们使用「moment.js」 工具包加快效率,moment.js 提供丰富的各类数据获取接口,方便快速调用,在项目内执行以下命令安装:yarn add moment 或 npm install --save moment
(4)Input
填写温度报警阈值需要输入框提供输入, Ant Design 提供「InputNumber 数宇输入框」组件进行输入,并对输入变化提供 onChange() API 接口方便实时获取到最新的输入值,提供最大最小值步长限定,提供小数点后6位的输入精度。
(5)Button
「Button 按钮」组件用于执行用户点击回调后的操作,需要取得时间选择框或者温度阈值输入框中的数值,将数值作为请求参数调用后端服务请求数据,通过提供的 onClick()回调函数 AP| 响应用户的点击。
(6)图表数据
使用 Bizcharts 图表展示设备的温湿度时序数据,在组件前面加入与 API 文档一致的数据,用 data 标识,此外报警阈值使用 threshold 标识。
(7)图标样式
Bizcharts 提供了丰富的 demo 源码可供参考学习,缺少双坐标轴的例程,只需要添加一个<Axis>标签用于展示设备湿度属性的坐标轴即可,示例如下(详细参见 exercise2.../component/Chart.js)。
时间横轴
<Axis name="gmtCreate" title />
温度竖轴
<Axis
name=" currentTemperature"
title
label={{ formatter: val
口
‘${val}°C' }}
湿度竖轴
<Axis
name="currentHumidity"
title
label={{ formatter: val =‘${val}%° }}
提示框
<Tooltip
// title={"hh"}
crosshairs={{
type: "y"
}}
/>
温度点
<Geom
type="line"
posit ion="gntCreate*currentTemperature"
size={4}
// shape={" smooth"}
color={"#0099ff"}
/>
湿度点
<Geom
type="line"
posit ion="gmtCreate*currentHumidity"
size={4}
color={"#33cc33"}
// shape={"smooth"}
/>
温度阀值辅助线
<Line
top={true}
//指定 guide 是否绘制在 canvas 最上层,默认为 false, 即绘制在最下层
start={['min', threshold]}
//辅助线起始位置,值为原始数据值,支持 callback
end={['max'
,
threshold]}
//辅助线起始位置,值为原始数据值,支持 callback
lineStyle={{
stroke:
‘
#FF5C68'
, //线的颜色
lineDash: [0, 2, 2]
, //虚线的设置
lineWidth: 3
//线的宽度
}} //图形样式配置
text={{
position: 'start',
//文本的显示位置
autoRotate: true
, //是否沿线的角度排布,默认为 true
style: { fillk "red" }
,//文本图形样式配置
content
:“温度报警阈值:”+ threshold + "e
”,//文本的内容
offsetY: -5
// y 方向的偏移量
}}
/>
4、Table 表格组件
(1)HistoricalAlarmTable
设备历史报警数据表格在设计草图中没有完整体现,使用 Ant Design 提供的表格组件即可展现历史报警的列表,通过 RangePicker 组件选择起止时间对报警数据进行查询。
(2)Table 组件
「Table 表格」组件提供行列的数据展示,并且提供丰富的 AP| 功能,通过 column 属性配置列的样式和内容,通过dataSource 配置数据源,通过 pagination 配置分页大小。
(3)「Column」配置
Colum 配置项较多,较为简单的配置实现 dataIndex (数据索引)、title (标题显示)属性即可,Table 组件会依据datalndex 中
配置的字段解析 dataSource 中的数据,title 为该 datalndex 需要显示的名称,为渲染出的表格的头部信息。在本期例程中,配置的
Colum 属性如下:
const columns = [{
title
: '设备名',
dataIndex: ' deviceName' ,
}
,
{
title
: ' 报警温度值',
dataIndex: ' alarmTemperature' ,
}
,
{
title
: '报警时温度阀值' ,
dataIndex: ' tempThreshold',
}
,
{
title
: ' 创建时间',
dataIndex: ' gmtCreate '
}];
(4)「dataSource」配置
dateSource 为 Table 的渲染内容,送入 JSONArray 类型的数据即可,JSONArray 的每个单元中的字段与、Column中的 datalndex 字段对应。
(5)「pagination」 配置
pagination 属性配置与 pagination 组件一致(实际渲染也是 pagination 组件),可通过 pageSize 属性设定每页显示的数据条数。
(6)表格组件详细内容
详细参考 Table.js 文件。
import React from ' react '
import ' antd/dist/antd.css';
import { Table, DatePicker, Button } from 'antd';
import moment f rom ' moment ' ;
const { RangePicker } = DatePicker;
const dateFormat = "MM-DD HH: mm:ss"
export default class HistoricalAlarmTable extends React. Component {
constructor(props) {
super(props);
初始化起止时间
this.state = {
beginTime: moment().startOf('month'). format('YYYY-MM-DD HH:mm:ss'),
endTime: moment(). format('YYYY-MM-DD HH:mm:ss'),
}
render() {
选择起止时间回调函数
const datePicker0nOk = (dates) => {
this. setState({
beginTime: dates [0]. format('YYY-MM-DD H:mm:ss'),
endTime: dates [1]. format('YYY-M-DD HH:mm:ss'),
});
const ranger0nClick = () => console. log
("点击起止时间提交" );
return (
<div>
<div style=(( paddingBottom: 20 }}>
选择查询时间
起止时间选择与提交
<RangePicker
defaultValue={ [moment().start0f( 'month'), moment()]}
ranges={{'
今天
': [moment().start0f('day'), moment()], '
本周
': [moment().start0f
on0k={datePicker0n0k}
format={dateFormat}
showTime=(true)
/>
<Button type="primary" style={{ display: ' abslute', top: 0 }}onClick={ ranger0nCl
提交
</Button>
</div>
<Table
columns={columns}
dataSource={data}
pagination={{ pageSize: 5}}
/>
</div>
)
}
}
到目前为止,完成了 UI 层的开发,阶段参考代码在仓库地址/exercise2文件夹下运行前命令行执行 yarn 安装项目依赖。
五、网络请求
到目前为止,主要使用物理搭建其项目的UI内容,而数据内容则需要依赖网络请求以及组件数据来进行控制。这节介绍网络请求工具 axios 和数据流框架 dva,
1、Axios 请求库
「axios」是流行的 HTTP 请求库,可快速集成在项目中,可以通过以下命令快速安装 axios:yarn add axios或npm install --save axios
axios 请求可以通过 async await 取得最终的请求返回数据。基于 axios 封装 get 和 post 的函数工具,在一级目录下新建 axios 文件夹,新建,tools.js 文件:
import axios from ' axios '
//通过 async 和 await 进行 promise 对象的处理
post 方法
export default {
async post(url, data) {
return await axios({
method: ' post' ,
url,
data: data,
//post 请求参数
timeout: 10000
,
//超时时间
})
},
get 方法
async get(url, data) {
return await axios({
method: 'get',
url,
params: data,
//get 请求时带的参数
timeout: 10000
,
//超时时间
})
}
2、Message 弹窗组件
Message 是页面全局提示组件,可以用来对于请求异常返回码进行弹窗,提示网络状况。编写函数对请求返回 code码进行判断。
判断返回码的函数
const checkStatus = (res) => {
if (res.status !== 200) {
message. error
('请求失败或网络异常,请检查...');
} else {
if (res.data.code !== 0) {
message.error(
'提交失败或服务器异常,请检查...');
}
}
}
3、API 封装
在 tool.js 中定义了请求的方法,将各请求写入组件将变得难以维护和管理,统一将 API 请求写进 axios/index.js。
import http from
‘
./tools';
import { message } from 'antd' ;
const url = 'ttp:/xxxxx/ap1/v1/device';
//请换成你的请求地址,可用请求地址 http://47.110.
检查返回码并进行错误弹窗
const checkStatus =(res) =>{
if (res.status !== 200) {
Messag.error
('请求失败或网络异常,请检查..
} else {
if (res.data.code !== 0) {
message.error
('提交失败或服务器异常,请检查...');
}
}
}
API 请求封装
//查询设备的列表
export const getDeviceList =async ()= > {
//得到原始输出
let res = await http.get(url + '/listDeviceName', null);
checkStatus(res);
return res.data;
}
//查询设备的属性
export const queryDeviceProp = async (params) ”=>{
let res = await http.get(url + '/queryDeviceProp', params);
checkStatus(res);
return res.data;
}
//查询设备的属性
export const selectChartDataByTime = async (params) = >{
let res = await http.get(url + '/queryDevicePropHistoryLogs', params);
checkStatus(res);
return res .data;
)
//设置报警阀值
export const setDeviceProperty = async (params) =>{
let res . await http.get(url + '/setDeviceProperty', params);
checkStatus(res);
return res.data
}
//查询历史报警记录
export const queryAlarmHistoryLogs = async (params) =>{
let res = await http.get(url + '/queryAlarmHistoryLogs', params);
checkStatus(res);
return res.data
}
//清除报警效果
export const clearAlarm = async (params) =>{
let res = await http.get(url + '/clearAlarm', params);
checkStatus(res);
return res. Data
}
将本次历程所需要的6套 API 分装成函数以准备进行调用。
六、dva 管理数据
1、问题:如何在 header 组件内选择不同设备,
主页自动请求跳转此设备相关的数据,即如何使一个组件状态可以影响到其他的组件?
dva提供了简单易用的解决方案。所有的组件存储在一个状态对象 state 中,各组件通过请求去改变 state。
2、在 dva 中有三个核心概念是 state,action 和 view。
State 是中心数据仓库,通过组件的 props 连接到组件内部,组件发生操作会 dispatch 一个 action 给 state,从而通知 state 完成改变。
随着前端组件增多,功能划分分散,所有的组件维护同一个 state 是非常危险的。Model 是 dva 中 state 的单元,每个前端功能块一个 model 实现解耦。
3、Model,state 的单元多个小的 model 的 state 以 namespace 为 key 合成,开发者可以依照业务逻辑,通过namespace 来对 state 进行划分,最终交给 dva 处理
model 的常用配置有 namespace. state、reducers、effects :
{
namespace:' count'
,
state: 0,
reducers: {
add(state) { return state + 1 },
}
,
effects: {
*addAfter1Second(action, { call, put }) {
yield call(delay, 1000);
yield put({ type: 'add' });
},
}
,
}
(1)namespace:
当前 Model 的名称。整个应用的 State,由多个小的 Model 的 State 以 namespace 为 key 合成。
(2)state:
该 Model 当前的状态。数据保存在这里,直接决定了视图层的输出。
(3)reducers:
Action 处理器,处理同步动作,用来算出最新的 state ( 必须
返回一个新的 state)
(4)effects:
Action 处理器,处理异步动作(进行网络请求的调用,再调用执行 reducers 刷新 state)。
4、编辑主页model
新建主页 model,新建 main/models/main.js,分为 namespace、state 、reducer 和 effect。
(1)引入封装好的 API 函数
import {
getDeviceList,
que ryDeviceProp,
se lectChartDataByTime,
setDeviceProperty,
queryAlarmHistoryLogs ,
clearAlarm
}from '../../../axios/index' ;
import ( message ] from ' antd'
;
(2)将各组件所需数据按照格式初始化在 state
//储存 model 的 state状态
state: {
deviceName: "test_ zhinengjiaju",
deviceSelectList: ["test_ zhinengjiaju"],
alarmCard:{
state: true,
recentAlarmTime: null
}
onlineCard:{
state: "
在线
",
recent0nlineTime: null
},
tempCard: {
value: 22,
threshold: 22
},
humiCard: {
value: 43,
sensor: "DHT11
温湿度传感器
"
},
updateTime: "
带初始化
",
chart: {
dataList: [],
},
table: {
data: []
}
}
(3)Reducer 为新的 state 合并到旧的函数
reducers: {
//自动合并新的状态到旧的状态
update(state, { payload }) {
let newState = payload;
return {
...state,
newState
};
},
},
{
devicName : "oldDevice",
deviceData : "oldDeviceData"
}
{
devicName: "newDevice",
}
{
devicName: "newDevice",
}
{
deviceData : "oldDeviceData"
)
[Example]State fonewStatus 做 merge
如 reducers 中有 state 和 devicedata 两个字段,当新的 state 请求到的数据直接去替换掉原有的 state 会造成devicedata 的字段的丢失。通过拓展运算符,3个点 newstate 的会变新 state 内容,两者合成会将新的 state 字段赋上原有的 state 数据,实现 state 内容的差分更新。
(4)建立主页 model, state、reducer 和 effect
reducer 是可以直接修改 state 的内容的接口,而 effect 则是对网络请求等义务的消息进行请求函数,请求结果需要依靠 reducer 间接修改 state 中的数据内容,通过 put 函数调用 reducer 当中的方法,通过将 API 请求返回的数据分装成为 state 中数据格式即可。
(5)Model 与组件的 connect
在 model 中定义了组件所需要的数据和网络请求函数,用 connect 将组件所需数据和请求函数绑定。「connect」将model 和组件联系在一起,可以让组件访问 state 中的数据,调用 state 中 reducer方法和effect方法,一个model可以 connect 多个组件,多个组件可以共用一整套数据,共享一整套方法,model 中数据的改动会实时改变在组件的UI显示上(通过 props 传入,组件对应的生命周期函数会被调用( componentWillReceiveProps) )。
七、dva 数据流转项目下载
Model 和组件 connect 后的项目示例代码打包在项目仓库/exercise3下,启动前通过 yarn 命令安装依赖。
八、项目打包
前面实现了 web 应用的搭建,只是项目运行在本地的开发电脑上,可以称之为开发环境。如何将前端应用部署在服务器上供所有人访问使用,接下来一起进行项目的部署和调试。前端项目文件多,体系复杂,在开发项目中有更多的文件,都能够就此中有数千个文件高达几百兆。一般使用打包工具对项目进行打包压缩,最终只需要部暑压缩后的文件。umi 集成了打包工具 webpack,在项目主目录下执行 umi build,webpack 会自动将项目的文件进行打包和压缩,一般打包时间在几十秒。
打包构建的输出在 dist 文件夹下,众多的js文件被压缩打包进 umi.js,CSS 被打包压缩进 umi.css,图片等前端素材被打包进 static 文件夹,打包后目录结构如下所示:
./dist/
index. html
static
yay. 44dd3333.jpg
umi.css
umi.js
九、项目部署
1、Nginx 服务器部署
执行 umi build 打包后的前端内容想要实现应用文件的高性能分发,
还需要将项目部暑到高性能服务器上去,「nginx」 是一款流行的免费开源服务器软件,配置简单,性能强大,经常在生产环境中使用。
2、Linux 环境下部署
(1)在命令行执行以下命令安装 nginx:
sudo apt-get install nginx
(2)修改/etc/nginx/nginx.config 配置文件内容
修改为 build 后文件夹路劲,通过80端口访问
(3)重启 nginx 服务:
service nginx start
访问 ip 地址,即可访问 nginx 部署好的项目内容。
3、Windows 环境下部署
windows 环境下 nginx 配置内容与 Ubuntu 基本一致,目录地址与
Ubuntu 稍有差异,文件系统地址需要改为“/", nginx 1.14版本「下
载地址」,下载解压后,修改 conf 文件夹下 nginx.config 内容即可,
点击解压文件夹下.exe 文件,即可启动 nginx,windows 环境下 nginx 的重启与关闭可以通过任务管理器执行。
通过本次的学习,基于后端服务搭建了前端的 web 应用,对于实时展示设备最新的数据,并且实现了这个设备的交互,实现了 ant design 组建了集成,页面组件的划分,基于 dva 把进行数据管理与展示,所有的组件集成于单个页面当中,也可以基于 lyout 组件组成多个页面,将智能家居的场景各个功能进行细化。
十、总结
现在已经学完了全部的课程内容,在本课程里一起学习了如何基于 stm32和阿里云 Iot 平台进行互联网应用的开发。首先一起了解了阿里云 Lot 平台特点,stm32产品系列和生态系统以及 mqtt 协议。在设备端开发这一部分,学习了如何使用 stm32cube 图形化工具初始化改 Stm32工程,如何对 paho 协议站进行适配以及阿里云 linkkit c-sdk 的适配和使用。
在最后的服务端应用开发部分,学习了如何基于 springboo kand practice mycircle,搭建后端数据库服务器,对于外部前端服务器开发学习的如何使用 umi 框架作为项目骨架,通过可视化组件实现了页面搭建,并采用 dva 框架进行数据管理。
课程章节 |
模块内容 |
详细目录 |
(一)课程指南 |
1、课程要解决的痛点 |
课程场景介绍,数据路径端到端 |
2、课程适用于不同资源水平的节点设备 |
低配版节点设备、高配版设备 |
|
3、课程所需具备的软、硬件 |
|
|
(二)阿里云 lot 平台介绍 |
1、物联网平台简介 |
物联网平台简介 |
2、物联网平台基础概念讲解 |
设备相关概念 |
|
平台相关概念 |
||
(三)基于 stm32的节点设备接入阿里云 lot 平台 |
1、基于stm32的节点端及开发环境介绍 |
STM32产品介绍:十四大家族和 IoT 策略
|
STM32生态系统介绍: STM32Cube |
||
STM32L4R5以及 Nucleo-L4R5介绍 |
||
ST senso 板和 EMW3080板介绍 |
||
基于 Paho MQTT 的直连(适用于资源受限设备)
|
Demo 运行起来 |
|
MQT 协议介绍 |
||
Demo 介绍 |
||
基于 Linkkit C-SDK 的 MQTT 直连(适用于资源丰富设备 ) |
Demo运行起来 |
|
Linkkit C-SDK 介绍 |
||
Demo 介绍 |
||
(四)服务器端的应用开发 |
1、综合软件架构介绍 |
软件架构介绍 |
知识结构梳理 |
||
2、后端服务开发 |
认识后端框架 |
|
初始化运行第一个后端项目 |
||
应用系统开发 |
||
应用调试与部署 |
||
3、前端服务开发体验 |
认识前端框架 |
|
初始化并运行第一个前端项目 创建和使用组件 |
||
使用 dva 实现数据流转 |
||
应用调试与部署 |
十一、课程场景概览
最后再来回顾一下本次课程的场景,在本课程的场景中实现了数据从节点端到云端用户应用程序的全链路交互。sdm32端作为设备端完成了前半程数据上传,也就是向云端上传节点的温湿度信息和设备状态,也就是高温报警这一部分硬件应用到了 sdm32L 425mutual 版。Stiks01a2传感器拓展版和信科 EMW3080ipad 拓展版。对于资源授信的设备和资源分配的设备,分别提供了通过 paho 协议站直连和通过阿里云 lot 平台的 Linkkit C-SDK 这两种方式与阿里云 Lot 平台进行连接。数据到阿里云 lot 平台测以后的后半程的处理,在系统中是通过搭建用户服务器和阿里云 Lot 平台通信,来完成数据的读取和控制面源的下发以及人机交互的过程。用户服务器包括前端服务器和后端服务器两个部分,前端负责完成和用户的人机交互这部分功能,后端负责和 Lot 平台通信维护数据库,因为前端服务提供数据。
1、每5秒上报温湿度值,闪烁绿灯
2、温度超[阈值]亮红灯,并在每10秒向用户服务器报警,直到温度恢复[阈值]以下或者收到警报解除消息
3、收到警报解除信息后红灯闪烁
4、温度恢复到[阈值]以下天红灯
5、湿度值被阿里云 |oT 转发到用户服务器,进行数据库存储,同时在 web 端显示近期温湿度数据曲线
6、报警消息被阿里云 |oT 转发到用户服务器,在web端显示
7、用户通过 web 端页面解除报警
8、用户通过 web 端页面设置[阈值]参数