看到那个elm的react-native练习项目还挺火的,所以想着用Redux来改造一下.
elm RN项目
- 主要添加有mock-server作为后端数据模拟
- Redux的作为前端数据层
- Redux-saga对于管理异步操作数据流
- immutable对于不可变数据的管理
- 后续添加一些其他的东西例如导航系统和表单系统
目的:在没有后台服务器的情况下,使用mock-server来模拟后台数据,形成完整的React-Redux的数据流.
后端数据的模拟
elm的RN的数据来源是一部分从data.js中获取,一部分是直接在页面中模拟.
这么做对于写页面没有问题,但是对于远程请求的问题就没有办法了. 如果是直接在页面中通过fetch函数来获取,也是可行的,但是一旦数据来源很多的时候,管理和编写代码就变得很麻烦.拿elm的首页来说,如果在实际中要从网络服务请求的数据非常多,后面会讲到.这时候有必要引入Redux来对数据的请求和组织做集中处理.我考虑的不仅仅是数据请求的代码问题,其实还包括速度的问题.这个后面也会说到.
我们直接到位,借用mock-server这个很牛的工具来模拟远程的数据.这个服务的设置和使用还是非常简单的.
我觉得在使用中你要清楚三个问题,一是node.js的一些基础知识,mock-server是用think.js框架写的,您没看错,的确不是thinkphp.呵呵.
这个框架里的写法和thinkphp很相像.可不是,名字都差不多.但是差的十万八千里了.有一点node.js的知识就行,我们不在这里写代码.二是如果遇到要真机调试的时候,懂代理的设置,意思是这个mock-server实际是运行在127.0.0.1下的,要想办法让外界的终端可以访问.当然如果是模拟器调试就没有问题.三是mysql数据库的,要能启动,mock-server是不自带mysql服务器的,需要自己启动和配置.这个我们按步骤来说明,mac下我使用的是mamppro这个软件,非常的方便.
首先安装mock-server服务器
非常简单没有什么难度.请参见简书文章:node.js web版 mock-server 让你更好的管理你的模拟数据
https://github.com/flftfqwxf/mockserver
- 依赖包npm安装
npm install
-
mysql数据库的安装
用了mac下的比较好的mamp pro这个集成环境,里面非常好用的是mysql的密码修改,很方便,总是记不住密码.使用brew 安装mysql也可以用.
- 根据mysql数据的用户名和密码更改mock-server的配置文件
src/common/config/db.js
mysql: {
host: '127.0.0.1',
port: '3306',
database: 'mockserver',
user: 'root', //根据你自己mysql用户名修改
password: 'YES', //根据自己的mysql密码修改
prefix: 'mock_',
encoding: 'UTF8MB4_GENERAL_CI'
},
导入.sql文件数据
进入文件夹目录运行
npm start
,运行在127.0.0.1:8003端口-
就可看到下面的后台界面了
我们新建一个elm的项目,后面的的api都放到这里
-
举个例子 ,我们想要一个列表API:
-
修改
-
设置响应参数
看到后面输出了统计结构,就可以使用了.
点击保存后,查看结果,地址栏就是api的链接,输出就是json数据
[
{
"name": "田老师红烧肉(知春路店)",
"isBrand": true,
"logo": 27,
"scores": 3.5,
"sale": 4013,
"bao": true,
"piao": true,
"ontime": true,
"fengniao": true,
"startPay": "¥20起送",
"deliverPay": "配送费¥4",
"evOnePay": "¥21/人",
"journey": "250m",
"time": "35分钟",
"activities": [
{
"key": "减",
"text": "满20减2,满30减3,满40减4(不与美食活动同享)"
},
{
"key": "特",
"text": "双人餐特惠"
}
]
},
{
"name": "稻麦香(金源店)",
"isBrand": true,
"logo": 21,
"scores": 2.5,
"sale": 2419,
"bao": true,
"ontime": true,
"startPay": "¥0起送",
"deliverPay": "配送费¥2",
"evOnePay": "¥11/人",
"journey": "150m",
"time": "25分钟",
"activities": [
{
"key": "减",
"text": "满20减2,满30减3,满40减4(不与美食活动同享)"
}
]
},
{
"name": "米有理由(中关村店)",
"logo": 11,
"scores": 1.5,
"sale": 1419,
"startPay": "¥0起送",
"deliverPay": "配送费¥5",
"evOnePay": "¥12/人",
"journey": "450m",
"time": "45分钟"
},
{
"name": "吉野家(鼎好店)",
"isBrand": true,
"logo": 16,
"scores": 4.5,
"sale": 3419,
"ontime": true,
"startPay": "¥0起送",
"deliverPay": "配送费¥2",
"evOnePay": "¥14/人",
"journey": "150m",
"time": "25分钟",
"activities": [
{
"key": "减",
"text": "满20减2,满30减3,满40减4(不与美食活动同享)"
},
{
"key": "新",
"text": "新品5折"
},
{
"key": "特",
"text": "双人餐特惠"
}
]
},
{
"name": "周大虾龙虾盖浇饭(中关村东路店)",
"logo": 18,
"scores": 4,
"sale": 4013,
"piao": true,
"ontime": true,
"fengniao": true,
"startPay": "¥20起送",
"deliverPay": "配送费¥4",
"evOnePay": "¥21/人",
"journey": "250m",
"time": "35分钟",
"activities": [
{
"key": "特",
"text": "双人餐特惠"
}
]
},
{
"name": "轰咖咖喱饭(中关村东路店)",
"isBrand": true,
"logo": 17,
"scores": 4,
"sale": 4013,
"piao": true,
"fengniao": true,
"startPay": "¥20起送",
"deliverPay": "配送费¥4",
"evOnePay": "¥21/人",
"journey": "250m",
"time": "35分钟"
}
]
一下的其他接口依次来处理,就不再多说了,这个讲的够详细了.需要注意json对象和javascript对象的写法的差异性.如果写不对json格式.可以在console里面使用JSON.stringify()函数来转一下(键名和键值加双引号).
这是个工具,我们接着这个工具来完成模拟数据的工作
用实际UI替代设计原型图
首页的截图
下面根据逻辑功能对可视的这一部分做一下分类标记, 屏幕下面没有显示的部分,我刻意没有标出来,这么做是从性能上考虑的,后面在获取远程数据的时候会讲到这一点.
逻辑分区的分解
1.地名和天气数据的获取
实际的编码中应该是先从本机的gps获取地里位置,接着可以获取到地名和天气信息.
getLocationData() //根据gps信息获取地里信息
{
"address": "三里屯CBD"
}
getWeatherData() //根据gps信息获取天气信息
{
"location": "北京",
"district": "朝阳",
"tempature": [
19.2,
25.3,
37
],
"pm2.5": 25,
"wind": "breeze",
"weather": "sunny",
"ultravolvet": "strong",
"clothing": "single"
}
2.关键词部分
//getkeywords()
{
"keywords": [
"肯德基","粥","必胜客","一品生煎","星巴克"
]
,
"string": "Hello World"
}
3.搜索,这一块没有获取数据,所以暂时不处理
4.swipe部分,这一部分的名称不动,但是可以根据网络状况选择在Wifi条件下去加载网络图片,在其他环境下从本地加载图片
5. 每日图片.
返回一张图片的url地址
//everydayPic
{
"everydaypic": {
"name": "测试",
"url": "https://ww1.sinaimg.cn/large/006tNbRwly1ffdm6jl0ylj30rs04t3yy.jpg",
"info": "测试信息",
"picID": "图片有关介绍"
}
}
6.推荐套餐
//recommend
{
"array": [
{
"热卖套餐": {
"keyword": "热卖",
"价格": 100,
"picurl": "https://ww4.sinaimg.cn/large/006tNc79ly1few6ck5q31j30rs06eabn.jpg"
}
},
{
"霸王餐": {
"keyword": "20元折扣",
"价格": 100,
"picurl": "https://ww4.sinaimg.cn/large/006tNc79ly1few6ck5q31j30rs06eabn.jpg"
}
},
{
"年货到家": {
"keyword": "年货节",
"价格": 100,
"picurl": "https://ww4.sinaimg.cn/large/006tNc79ly1few6ck5q31j30rs06eabn.jpg"
}
},
{
"5折优惠": {
"keyword": "5折优惠",
"价格": 100,
"picurl": "https://ww4.sinaimg.cn/large/006tNc79ly1few6ck5q31j30rs06eabn.jpg"
}
}
],
"keywords": "推荐套餐"
}
在首页可视的部分加载了几种数据,在整个页面中还有内容没有加载完,是不是考虑也要加载?肯定要的,但是这个可视的地方就是一个界限,为了保证速度,我觉得首先加载可视部分,不可见的部分稍后加载.在javascript的意思就是,先加的入队操作,后加的后操作,但是分开步骤来分布加载,首页加载的压力就小了.为什么要考虑这个问题?在f8 app中就出现这问题,作者指出来了.
在f8 app的 f8APP.js文件(相当于初始化文件)中有这样的代码:
// TODO: Make this list smaller, we basically download the whole internet
//这个地方在先于UI组件之前加载了所有的state,
//这里可以加载一部分首先需要的数据
this.props.dispatch(loadNotifications());
this.props.dispatch(loadMaps());
this.props.dispatch(loadConfig());
this.props.dispatch(loadSessions());
this.props.dispatch(loadFriendsSchedules());
this.props.dispatch(loadSurveys());
在这个文件中使用了redux的dispatch方法,加载了大量的数据.要考虑到分别加载的问题. 这就是需要解决的问题出发点.
我们同样也可以在一个入口文件(意思就是程序启动的文件,怎么能这样做呢?参考一下f8 app的做法吧.)中来加载首要显示的内容,不在可视区的数据我们在Home.js的生命周期函数中再加载.
这是一个解决性能问题的思路.供参考.
先写这么多. 利用好这些mock类的程序.这样把前后端的问题可以联系起来考虑.我这里指的是数据流的问题.这个流程和UI设计是独立的.
在这里前端人员掌握mock-server的好处是,你可以根据模拟的接口来思考当你在React程序中引入Redux以后怎么来组织state树中的数据. 因为Redux中数据是单向从store中流向React的UI组件的,state有个整体性,所以需要整体考虑.当然这里是比较简单的,如果是社交和电商软件,需要考虑到数据的交叉引用问题,这就不是我一人能思考的问题了.在mock-server项目中的数据结构可以以利于你形成下面的state的基本结构. UI组件需要的数据都从下面这张图中来获取.