react-ele-webapp
項目地址 :https://github.com/kliuj/react-ele-webapp
run
下載完項目
npm install
然后
npm run dev
即可
基于 react react-router redux
的項目,主要是為了學(xué)習(xí)實戰(zhàn)react
陪白。數(shù)據(jù)都是固定的买窟,從餓了么接口臨時抓的,模擬了一個0-100ms
的異步數(shù)據(jù)延遲谓厘,感謝餓了么磁浇。
以下內(nèi)容是項目開發(fā)的過程和一些思考求冷,按照這個過程至少能保證實現(xiàn)一個相對完整的react
全家桶項目
內(nèi)容參考
react
文檔:http://reactjs.cn/react/docs/getting-started-zh-CN.html
react-router
文檔地址 :https://reacttraining.com/react-router/web/guides/quick-start
react-router
中文版參考:http://www.uprogrammer.cn/react-router-cn/index.html
redux
文檔參考:http://redux.js.org/
redux
中文文檔:http://cn.redux.js.org/
搭建項目:
建立項目目錄,安裝package.json翰撑,配置webpack.config
做好基礎(chǔ)依賴工作罩旋,摘自package.json的一部分內(nèi)容
"devDependencies": {
"babel-core": "^6.23.1",
"babel-loader": "^6.4.0",
"babel-preset-es2015": "^6.22.0",
"babel-preset-react": "^6.23.0",
"html-webpack-plugin": "^2.28.0",
"jshint": "^2.9.4",
"jshint-loader": "^0.8.4",
"react": "^15.2.0",
"react-dom": "^15.2.0",
"react-router": "^2.0.0",
"redux": "^3.6.0",
"webpack": "^2.2.1",
"webpack-dev-server": "^2.4.1"
} //JAVASCRIPT
項目模塊結(jié)構(gòu)組織一些基礎(chǔ)工作
開始進行開發(fā)一個項目除了技術(shù)選型之外,還有許多基礎(chǔ)東西要先設(shè)計好眶诈,一個好的組織設(shè)計要可以為以后的提高工作效率涨醋。我這方面還有很多欠缺,目前主要考慮了3個模塊的設(shè)計:
1:后臺接口通信層:
model.js
主要處理統(tǒng)一接口的請求發(fā)送和回調(diào)册养,放在一起更有利于后期維護东帅,也增加可閱讀性
//接口對應(yīng)的url,這里只做演示
const uris = {
index_entry : fetchData.index_entry,
hot_search_words : fetchData.hot_search_words
}
//接口調(diào)用層
export default function send(url,postData,successCallback,errCallback){
//模擬延遲球拦,假接口
let promise = new Promise(function(resolve,reject){
setTimeout(function(){
resolve(fetchData[url])
},Math.random()*100)
})
promise.then(function(data){
successCallback(data)
})
}
2:本地數(shù)據(jù)緩存維護:
baseStore.js
主要處理頁面之間的跳轉(zhuǎn)返回靠闭,增加更多的自主性和擴展性
// 自動存儲瀏覽記錄
export function saveFrom(prop) {
let name = prop.pagename,
transit = prop.location,
qhfrom = transit.query.qhfrom ,//默認全部返回首頁
para = transit.query.para ? JSON.parse(transit.query.para) : '';
if(!qhfrom) return false;
let paths = localStorage.getItem("FromUrlStore") ? JSON.parse(localStorage.getItem("FromUrlStore")) : {};
if (localStorage) {
paths[name] = {
'name':qhfrom,//存儲來源頁面
'para':para //存儲來源頁面的參數(shù)
}
localStorage.setItem("FromUrlStore", JSON.stringify(paths));
}
}
//存儲頁面的來源,統(tǒng)一管理
3:公共方法的處理:
baseFun.js
主要用來定義一些公用的模塊方法
//放置公用函數(shù)
export function showToast(){
...
}
使用react-router初始化頁面
import React from 'react'
import { render } from 'react-dom'
import { Router, Route, Link,hashHistory ,IndexRedirect,IndexRoute} from 'react-router'
import Home from './components/home.jsx'
import Discover from './components/discover.jsx'
const App = React.createClass({
render() {
return (
<div>
<footer>
<Link to="/home">外賣</Link>
<Link to="/discover?qhfrom=home">發(fā)現(xiàn)</Link>
</footer>
{this.props.children}
</div>
)
}
})
const route = (
<Router history={hashHistory}>
<Route path="/" component={App}>
<IndexRoute component={Home} />
<Route path="home" component={Home} />
<Route path="discover" component={Discover} />
</Route>
</Router>
)
render(route, document.getElementById("app"))
代碼簡單介紹:
因為沒有后臺坎炼,采用的 hashHistory
(hash
路由)愧膀,關(guān)于hash
路由可以參考:https://github.com/kliuj/spa-routers 有簡單的介紹。
這個是router的跳轉(zhuǎn) <Link to="/home">外賣</Link>
這個是加載子路由組件 {this.props.children}
這個是默認的跳轉(zhuǎn)頁面 <IndexRoute component={Home} />
處理首頁的滾動列表
首頁主要分成了4個組件
底部導(dǎo)航 + 滾動列表 + 單個產(chǎn)品 + 首頁搜索框
滾動列表封裝了一個簡單的組件
<List
list={Pro} //每個產(chǎn)品item組件
pagename={'home'} //跳轉(zhuǎn)產(chǎn)品列表的上級頁面 用來處理返回
data={this.state.productList} //需要渲染的數(shù)據(jù)
onScroll = {this.getMore.bind(this)}//滾動加載函數(shù)
/>
在scrollList組件里面監(jiān)聽了滾動事件進行自動加載的處理
react-redux 處理登錄和登出
使用redux的原因:用戶信息和登錄是兩個不同的組件谣光,也沒有父子級的關(guān)系檩淋,但是需要進行數(shù)據(jù)狀態(tài)共享和相互影響。詳細信息可以看上面的官方文檔,我這里就簡單說一下我這個項目的應(yīng)用蟀悦。
定義常量
actionTypes.js
//登入成功
export const LOG_SUCCESS = 'LOG_SUCCESS'
//正在登錄
export const LOG_ING = 'LOG_ING'
//注銷登錄
export const LOG_OUT = 'LOG_OUT'
//主要是統(tǒng)一保存狀態(tài)對應(yīng)的名稱
定義具體的觸發(fā)操作
actions/login.js
//注銷 同步
export function log_out (){
return {
type:actionTypes.LOG_OUT
}
}
//登入 異步
export function log_in (obj){
return dispatch=>{
//pending 正在進行登錄的狀態(tài)
dispatch({type:actionTypes.LOG_ING})
//開始發(fā)送異步請求登錄
new Promise((resolve,reject)=>{
...
}).then(res=>{
dispatch(res)
})
}
}
//異步狀態(tài)需要使用中間件
處理數(shù)據(jù)
reducers/login.js
export default function(state = initialData,action){
switch(action.type){
case actionTypes.LOG_SUCCESS:
return {
loginstate:1,
username:action.username
}
break
case actionTypes.LOG_ING:
return{
loginstate:-1,
username:'正在登錄'
}
case actionTypes.LOG_OUT:
return initialData
break
default :
return initialData
}
}
使用中間件創(chuàng)建store層
store/store.js
import {createStore, applyMiddleware} from 'redux'
import thunk from 'redux-thunk'
//合并的多個reducer媚朦,解耦
import rootReducer from '../reducers/index.js'
const middlewares = [thunk]
const createStoreWithMiddleware = applyMiddleware(...middlewares)(createStore)
export default function configureStore(initialState){
return createStoreWithMiddleware(rootReducer,initialState)
}
在路由層引入
import {Provider} from 'react-redux'
const store = configureStore()
const route = (
<Provider store={store}>
<Router history={hashHistory}>
...
</Router>
</Provider>
)
組件里面使用
import { connect } from 'react-redux'
import {log_out} from '../../actions/login.js' //操作
...
...
function mapStateToProps(userinfo){
let {login} = userinfo //這個是返回的所有reducer,我們只用當(dāng)前需要的日戈,參考 reducers/index.js 內(nèi)容
return login
}
export default connect(mapStateToProps)(UserInfo)
//這個時候就可以在當(dāng)前組件狀態(tài)的 this.props 獲取到這個 login 數(shù)據(jù)询张,
//操作的時候
const {dispatch} = this.props;
dispatch(log_out())
//這時候就可以操作redux狀態(tài)的數(shù)據(jù),每次數(shù)據(jù)改變都會下發(fā)給所有接收的組件