zoukankan      html  css  js  c++  java
  • 【大众点评】—— 首页开发

    前言:正在学习react大众点评项目课程,学习react、redux、react-router构建项目。


    一、页面分析和组件划分

    二、组件开发

    走马灯效果 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>
    

    注:项目来自慕课网  

    人与人的差距是:每天24小时除了工作和睡觉的8小时,剩下的8小时,你以为别人都和你一样发呆或者刷视频
  • 相关阅读:
    如何卸载VS 2017之前版本比如VS 2013、VS2015、 VS vNext?
    在SQL Server中如何进行UPDATE TOP .....ORDER BY?
    EntityFramework 6.x和EntityFramework Core插入数据探讨
    2017-2018:时间戳
    http协议进阶(六)代理
    认清性能问题
    <转>安全测试思维导图
    RESTful API浅谈
    http协议进阶(五)连接管理
    聊聊软件测试的职业规划
  • 原文地址:https://www.cnblogs.com/ljq66/p/14391204.html
Copyright © 2011-2022 走看看