前言

  最近一段时间一直在学习React的相关知识,,这次决定用React去模仿一个移动端App,看了很多自己用过的App,最后决定模仿做饿了么移动端App,写下这篇博客,记录实践中的心得体会

项目构建

基于react、react-redux、react-router、antd、mongodb实现单个商家页部分,后台数据通过nodejs模拟实现。
1.安装脚手架工具 (单文件组件项目生成工具) 只需要安装一次

1
npm install -g create-react-app   /  cnpm install -g create-react-app

2.创建项目 (可能创建多次)

找到项目要创建的目录:

create-react-app reactdemo

3.cd 到项目里面

cd  reactdemo


npm start  运行项目


npm run build 生成项目

项目目录文件

使用插件

1、react(组件编写)

2、react-router(控制路由3.x)

3、react-dom(渲染dom)

4、redux(控制状态)

5、react-redux(更新组件状态)

6、react-promise(异步处理)

7、react-tap-event-plugin(手机tap事件)

8、ant-design-mobile(移动端ui组件)

9、better-scroll(页面滚动)
1.package.json

1
2
3
4
5
6
7
8
9
10
"dependencies": {
"antd": "^2.11.2",
"antd-mobile": "^1.4.1",
"axios": "^0.16.2",
"md5": "^2.2.1",
"react": "^15.6.1",
"react-dom": "^15.6.1",
"react-router-scroll": "^0.4.2",
"touchjs": "^0.2.14"
},

2.入口app.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
import React, { Component } from 'react';
import './component/static/css/antd.css';
import "./component/static/css/reset.css";
import "./component/static/css/common.css";
import $ from "jquery";
import Footer from "./component/footer";
import Order from './component/order';
import User from './component/user';
import Find from './component/find';
import Home from "./component/home";
class App extends Component {
render() {
return (
<div className="App">


{this.props.children}

</div>
);
}
}

export default App;

3.index.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
import React from 'react';
import ReactDOM from 'react-dom';
import "./component/static/css/index.css";
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import {Router,Route,Link,IndexRoute,browserHistory} from 'react-router'
import Order from './component/order';
import User from './component/user';
import Collection from './component/user/collection';
import Login from './component/user/Login';
import Reg from "./component/user/Reg";
import Reg1 from "./component/user/Reg1";
import Reg2 from "./component/user/Reg2";
import Find from './component/find';
import Search from './component/search';
import Home from "./component/home";
import Seller from "./component/home/seller";
import Detail from "./component/detail";
import Collect from "./component/collect";
ReactDOM.render(
<Router history={browserHistory}>
<Route path="/" component={App}>
<IndexRoute component={Home}/>
<Route path="home" component={Home}>

</Route>
<Route path="find" component={Find}>

</Route>
<Route path="order" component={Order}>

</Route>
<Route path="user" component={User}>

</Route>
<Route path="user/collection" component={Collection}></Route>
<Route path="user/login" component={Login}></Route>
<Route path="login" component={Login}></Route>
<Route path="reg" component={Reg}></Route>
<Route path="user/reg" component={Reg}></Route>
<Route path="reg1" component={Reg1}></Route>
<Route path="user/reg1" component={Reg1}></Route>
<Route path="reg2" component={Reg2}></Route>
<Route path="user/reg2" component={Reg2}></Route>
<Route path="user/collect" component={Collect}></Route>
<Route path="/quite" component={User}></Route>
<Route path="seller/:aid" component={Seller}></Route>
{/*<Route path="seller" component={Seller}></Route>*/}
<Route path="search" component={Search}></Route>
<Route path="detail" component={Detail}></Route>
</Route>
</Router>,
document.getElementById('root')
);
registerServiceWorker();

browserHistory去路径的#号
IndexRoute设置默认路由
4.storage.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
var app={

set:function(key,value){
localStorage.setItem(key,JSON.stringify(value));
},
get:function(key){
return JSON.parse(localStorage.getItem(key));

},
hasCollectionData(aid,collectdata){ /*判断collectdata里面有没有数据*/


//forEach是个异步

for(var i=0;i<collectdata.length;i++){

if(aid==collectdata[i].aid){

return true;
}
}

return false;


}
}

export default app;

localstorage方法封装

项目详情

主页

使用Swiper实现轮播图效果
flex布局

商家列表

商家列表通过下拉更新请求pai接口从数据库获取模拟数据渲染页面

搜索页面

历史搜索使用localstorage保存本地进行渲染

1
2
3
4
5
6
7
8
9
10
11
12
13
14
router.post("/search",function(req,res,next){

var keyword=req.body.data
DB.Find("trade",{$or:[{"title":{ $regex:new RegExp(keyword)}},{"name":{ $regex:new RegExp(keyword)}}]},function(err,data){

if(data.length>0){
res.json(data)
}else {
res.json(404)
}

})

})

服务端通过nodejs的模糊匹配搜索数据库符合的商家进行调整至商家详情页面

用户个人中心

用户注册页面

1.通过验证码点击事件判断,获取用户输入的账号,查询数据库是否已经存在,如果不存在会返回验证码信息
2.通过确认点击事件,获取用户输入的账号信息和验证码是否正确,如果正确保存在数据库,提示注册成功。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
router.post('/doReg1', function(req, res, next) {
Rphone = req.body.getphone;
var longstr = '1,2,3,4,5,6,7,8,9,0,A,B,C,D,E,F,G,H,I,G,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z';
var str = longstr.split(',');
var prevNum = '';
for(var i = 0; i < 6; i++) {
prevNum += str[Math.floor(Math.random() * 36)];
}
DB.Find("user",{phone},function(err,data){
if(err){
console('error');
}else {
phone=Rphone
if(data==""){
res.json({'msg':"手机号可用","status":"1","prevNum":prevNum})
}else{
res.json({'msg':"手机号已被注册","status":"0"})
}
}
})

})

用户登录页面

用户订单页面

用户已经登录未购买商品页面


用户未登录页面

用户登录并且购买过商品页面
通过判断localstorage的是否存在来判断用户是否登录,如果登录通过localstorage的信息查询数据来改变state的状态并且显示不同的页面。

loading加载页面

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
.load-top{
background: url("../img/loading.png") no-repeat;
width:.5rem;
height:.5rem;
background-size: 100% auto;
position:absolute;
background-size: 100% auto;
transform-origin: 50% 50%;
animation: change 3.6s infinite steps(6),trans .3s ease-in-out infinite alternate;
}
.w12{
width:.22rem;
border-radius: 30%;
}
.load-bot{
background: url("../img/load.png") no-repeat;
top: 50%;
width:.5rem;
height:.5rem;
background-size: 100% auto;
position:absolute;
background-size: 100% auto;
transform-origin: 50% 50%;
background-position-y: .2rem;
animation: scale .3s ease-in-out infinite alternate;
}
@keyframes scale{
100%{
transform: scale(.3);
}
}
@keyframes change{
100%{
background-position: 0 -3rem;
}
}
@keyframes trans{
100%{
transform: translateY(-2.5em)
}

通过css3动画实现

商家商品详情页面

使用fly实现购物车飞入效果,右上角为用户收藏事件,通过storage拿到用户信息查询数据库搜索用户收藏列表返回状态改变,用户点击以后再更改用户收藏店铺表的状态。



购物车和付款页面,通过redux实现用户跳转订单页面没有购买调回,用户购物车信息不丢失,如果用户付款成功清空rendux的购物车,并且跳转至订单页面;

订单页面

用户收藏页面