
一、页面分析和组件划分

二、组件开发
走马灯效果 react-slick
npm install react-slick --save
//public ->index.html
<link rel="stylesheet" type="text/css" charset="UTF-8" href="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.6.0/slick.min.css" />
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/slick-carousel/1.6.0/slick-theme.min.css" />
class命名规范:BEM命名规范
<ins></ins> 被插入的文本<del></del> 被删除的文本
猜你喜欢 加载更多的效果

//LikeList->index.js
import React, { Component } from "react";
import LikeItem from "../LikeItem";
import Loading from "../../../../components/Loading";
import "./style.css";
class LikeList extends Component {
constructor(props) {
super(props);
this.myRef = React.createRef();
this.removeListener = false;
}
render() {
const { data, pageCount } = this.props;
return (
<div ref={this.myRef} className="likeList">
<div className="likeList__header">猜你喜欢</div>
<div className="likeList__list">
{data.map((item, index) => {
return <LikeItem key={index} data={item} />;
})}
</div>
{pageCount < 3 ? (
<Loading />
) : (
<a className="likeList__viewAll">查看更多</a>
)}
</div>
);
}
componentDidMount() {
if(this.props.pageCount < 3 ) {
document.addEventListener("scroll", this.handleScroll);
}else {
this.removeListener = true;
}
if(this.props.pageCount === 0) {
this.props.fetchData();
}
}
componentDidUpdate() {
//使用加载更多功能2次后解除绑定,不再使用scroll监听事件
if (this.props.pageCount >= 3 && !this.removeListener) {
document.removeEventListener("scroll", this.handleScroll);
this.removeListener = true;
}
}
componentWillUnmount() {
//如果没有移除scroll监听事件。需要手动移除
if (!this.removeListener) {
document.removeEventListener("scroll", this.handleScroll);
}
}
// 处理屏幕滚动事件,实现加载更多的效果
handleScroll = () => {
//获取页面滚动的距离 (兼容不同的浏览器)
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
//获取屏幕可视区域的高度
const screenHeight = document.documentElement.clientHeight;
//获取LikeList页面距离顶部的距离、LikeList页面高度
const likeListTop = this.myRef.current.offsetTop;
const likeListHeight = this.myRef.current.offsetHeight;
if (scrollTop >= likeListHeight + likeListTop - screenHeight) {
this.props.fetchData();
}
};
}
export default LikeList;
//components->Loading->index.js
import React, { Component } from 'react';
import "./style.css"
class Loading extends Component {
render() {
return (
<div className="loading">
<div className="loading__img"/>
<span>正在加载...</span>
</div>
);
}
}
export default Loading;
三、redux状态管理 定义首页所需状态
Redux模块设计
1.设计state
redux->modules->home.js
const initialState = {
//猜你喜欢相关state
likes: {
isFetching: false, //正在获取数据
pageCount: 0, //分页加载
ids: [], //猜你喜欢的每一个产品的id
},
//获取超值特惠相关state
discounts: {
isFetching: false,
ids: [],
}
}
2.定义Actions
import {get} from "../../utils/request"
import url from "../../utils/url"
import { FETCH_DATA } from "../middleware/api"
import { schema } from "./entities/products"
//请求参数使用到的常量对象
export const params = {
PATH_LIKES:'likes',
PATH_DISCOUNTS: 'discounts',
PAGE_SIZE_LIKES: 5,
PAGE_SIZE_DISCOUNTS: 3
}
export const types = {
//获取猜你喜欢请求
FETCH_LIKES_REQUEST: "HOME/FETCH_LIKES_REQUEST",
//获取猜你喜欢请求成功
FETCH_LIKES_SUCCESS: "HOME/FETCH_LIKES_SUCCESS",
//获取猜你喜欢请求失败
FETCH_LIKES_FAILURE: "HOME/FETCH_LIKES_FAILURE",
//获取超值特惠请求
FETCH_DISCOUNTS_REQUEST: "HOME/FETCH_DISCOUNTS_REQUEST",
//获取超值特惠请求成功
FETCH_DISCOUNTS_SUCCESS: "HOME/FETCH_DISCOUNTS_SUCCESS",
//获取超值特惠请求失败
FETCH_DISCOUNTS_FAILURE: "HOME/FETCH_DISCOUNTS_FAILURE",
}
//加载的异步actions
export const actions = {
//加载猜你喜欢的数据
loadLikes: () => {
//返回一个函数,通过redux-thunk中间件进行处理
return (dispatch, getState) => {
const {pageCount} = getState().home.likes;
const rowIndex = pageCount * params.PAGE_SIZE_LIKES;
const endpoint = url.getProductList(params.PATH_LIKES, rowIndex, params.PAGE_SIZE_LIKES)
//发送Action creator
return dispatch(fetchLikes(endpoint))
}
},
//加载特惠商品
loadDiscounts: () => {
return (dispatch, getState) => {
const endpoint = url.getProductList(params.PATH_DISCOUNTS, 0, params.PAGE_SIZE_DISCOUNTS)
return dispatch(fetchDiscounts(endpoint))
}
}
}
//定义action creator
const fetchLikes = (endpoint) => ({
[FETCH_DATA]: {
types: [
types.FETCH_LIKES_REQUEST,
types.FETCH_LIKES_SUCCESS,
types.FETCH_LIKES_FAILURE
],
endpoint,
schema
}
})
const fetchDiscounts = (endpoint) => ({
[FETCH_DATA]: {
types: [
types.FETCH_DISCOUNTS_REQUEST,
types.FETCH_DISCOUNTS_SUCCESS,
types.FETCH_DISCOUNTS_FAILURE
],
endpoint,
schema
}
})
utils->url.js
export default {
//额外的一个类型参数:path ->rest常用的区分不同资源的命名方式
//获取的是第几条记录 ,每页获取多少条记录
getProductList: (path, rowIndex, pageSize) =>
`/mock/products/${path}.json?rowIndex=${rowIndex}&pageSize=${pageSize}`
}
3.定义Reducers(处理相应的action types)
import { combineReducers } from "redux";
//定义reducer 处理猜你喜欢的action type
const likes = (state = initialState.likes, action) => {
switch(action.type) {
case types.FETCH_LIKES_REQUEST:
return {...state, isFetching: true};
case types.FETCH_LIKES_SUCCESS:
return {
...state,
isFetching: false,
pageCount: state.pageCount+1,
ids: state.ids.concat(action.response.ids)
};
case types.FETCH_LIKES_FAILURE:
return {...state, isFetching: false}
default:
return state;
}
}
//定义reducer 处理特惠商品的action type
const discounts = (state = initialState.discounts, action) => {
switch(action.type) {
case types.FETCH_DISCOUNTS_REQUEST:
return {...state, isFetching: true};
case types.FETCH_DISCOUNTS_SUCCESS:
return {
...state,
isFetching: false,
ids: state.ids.concat(action.response.ids)
};
case types.FETCH_DISCOUNTS_FAILURE:
return {...state, isFetching: false}
default:
return state;
}
}
const reducer = combineReducers({
discounts,
likes
})
export default reducer;
首页组件连接Redux
- Redux中定义view层组件需要的Selectors函数,从Redux中获取组件需要的相关状态
- 容器组件中定义mapStateToProps、mapDispatchToProps,用于在Redux中将Redux的state转化为组件的props,将Redux的action也转化为组件的props的相关方法
- 组件使用相关的props进行数据的展示和数据的更改
1.定义Selector
redux->modules->home.js
//Selectors
//获取猜你喜欢的state
export const getLikes = state => {
return state.home.likes.ids.map(id => {
return state.entities.products[id] //数组类型保证数据展示的有序性
})
}
//获取特惠商品的state
export const getDiscounts = state => {
return state.home.discounts.ids.map(id => {
return state.entities.products[id]
})
}
//猜你喜欢当前分页吗
export const getPageCountOfLikes = state => {
return state.home.likes.pageCount
}
2.mapStateToProps、mapDispatchToProps
//帮助使用props调用action,不是使用dispatch调用action
import { bindActionCreators} from 'redux'
//高阶组件(函数),完成组件和redux层的连接
import { connect } from 'react-redux'
……
//actions 和 selectors
import { actions as homeActions, getLikes, getDiscounts, getPageCountOfLikes} from '../../redux/modules/home'
class Home extends Component {
render() {
const {likes, discounts, pageCount} = this.props
return (
<div>
<HomeHeader />
<Banner />
<Category />
<Activity />
<Headline />
<Discount data={discounts} />
<LikeList data={likes} pageCount={pageCount} fetchData={this.fetchMoreLikes}/>
<Footer />
</div>
);
}
ComponentDidMount() {
this.props.homeActions.loadDiscounts();
}
fetchMoreLikes = () => {
this.props.homeActions.loadLikes()
}
}
const mapStateToProps = (state, props) => {
//返回在容器型组件中使用到的对象,通过selector获取到的属性都会挂载到props下
return {
likes: getLikes(state),
discounts: getDiscounts(state),
pageCount: getPageCountOfLikes(state)
}
}
const mapDispatchToProps = dispatch => {
return {
homeActions: bindActionCreators(homeActions, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Home);
3.组件使用
不再在组件内维护dataSource和单独的state
Discount->index.js
const { data } = this.props;
LikeList->index.js
const {data, pageCount} = this.props;
componentDidMount() {
document.addEventListener("scroll", this.handleScroll);
this.props.fetchData();
}
componentDidUpdate() {
//使用加载更多功能2次后解除绑定,不再使用scroll监听事件
if(this.props.pageCount >=3 && !this.removeListener) {
document.removeEventListener("scroll", this.handleScroll);
this.removeListener = true;
}
}
Redux作为数据缓存层的作用
redux->modules->home.js
//加载特惠商品
loadDiscounts: () => {
return (dispatch, getState) => {
//redux数据缓存层的使用
const {ids} = getState().home.discounts;
if(ids.length > 0){
return null
}
const endpoint = url.getProductList(params.PATH_DISCOUNTS, 0, params.PAGE_SIZE_DISCOUNTS)
return dispatch(fetchDiscounts(endpoint))
}
}
LikeList->index.js
componentDidMount() {
if(this.props.pageCount < 3){
document.addEventListener("scroll", this.handleScroll);
}else{
this.removeListener = true;
}
if(this.props.pageCount === 0){
this.props.fetchData();
}
}
四、集成React Router
Router基本结构定义
containers->App->index.js
import { BrowserRouter as Router, Route, Switch} from 'react-router-dom';
<Router>
<Switch>
<Route path="/" component={Home} />
</Switch>
</Router>
添加首页到其它页面的Link
HomeHeader->index.js
import { Link } from 'react-router-dom';
<Link to="/search" className="homeHeader__search">输入商户名、地点</Link>
<Link to='/user' className="homeHeader__self">
<div className="homeHeader__portrait"></div>
</Link>
Discount->index.js
import { Link } from 'react-router-dom';
<Link key={item.id} to={`/detail/${item.id}`} className='discount__item'>
……
</Link>
LikeItem->index.js
import { Link } from 'react-router-dom';
<Link className="likeItem" to={`/detail/${id}`}>
……
</Link>
















