Skip to content

well-monkey/dazhongdianping

Repository files navigation

大众点评App

项目介绍

仿照大众点评APP,以React框架为基础开发的一套WebApp

软件架构

软件架构说明

教程说明

使用说明

学习说明


第1章 导学

    1-1 课程简介

        课程概述

            做什么?    ——模拟大众点评 webAPP
            那些功能?  ——列表,搜索,购买,评价
            技术        ——React+React-router+Redux

        前台 系统首页 选择城市 搜索页面 详情页面 收藏购买 登录页面
        后台 用户中心 评价功能

        技术点介绍

            构建工具 webpack babel less postcss 
            系统构架 React React-router Redux
            数据交互 fetch mock
            其它辅助 npm git 

        课程安排 

            环境搭建
            基础知识
            实战开发

        学习前提

            了解JavaScript css html 基础
            使用过npm webpack less es6 git 
            有至少一个完整的项目经验

        讲授方式

            先基础,后实战
            在讲解的同时引导思考,会抛出自己独特的观点
            一行一行写代码
            顺序观看

        课程收货

            如果使用React开发web系统
            学习前端组建化
            学会如果从零搭建一个前端开发环境


    1-2 学习目的

        webpack+React开发环境
        windows 最好装一个xshell 模拟linux命令的工具

            npm run dev 启动服务
            npm run build 打包 
            http-server -p 8081 进入目录以后监听端口8081 本地启动服务

        vendeor.js 是第三方库的js
        app.js 是自己手写的js


    1.6 安装插件

        readme.md 最好有这个 保留一些内容  程序+数据+文档
        查看版本是否一致
        node -v
        npm -v

        mkdir test 
        cd test 
        ll
        npm init 
        vi package.json

        npm install webpack webpack-dev-server --save-dev
        npm i react react-dom --save

        --save 和 --save-dev 的区别
        npm i时使用--save和--save-dev,可分别将依赖记录到package.json中的dependencies和devDependencies下面
        dependencies下记录的是项目在运行时必须依赖的插件,常见的例如react jquery等,即及时项目打包好了、上线了,这些也是需要的,否则程序无法正常执行

    1.7介绍webpack config.js(1)

        js文件,符合commonJs规范,最后输出一个对新昂,即module.exports{}
        node环境下运行  path 是node.js自带的库 
        package.json 里面安装的包 都可以require进来


    1.8介绍webpack config.js(2)

        module.exports{}
        entry 输入
        output  输出
        resolve 引入文件 可以不写后缀名
        module使用不同的loader  loaders加载器
        postcss  使用autoprefixer  使用时候加一些前缀
        plugins  插件
        
            // html 模板插件  指定目录开发环境调试和打包 自动把文件信息插入到这个模板里面 webpack自己做
            new  HtmlWebpackPlugin({
                template:__dirname + '/app/index.tmpl.html'
            })
            // 热加载插件  更新页面内容会发生变化
            new webpack.HotModuleReplacementPlugin()
            // 打开浏览器
            new OpenBrowserPlugin({
                url:'http://localhost:8080'
            })
        devServer
            colors:true //输出的结果为彩色
            historyApiFallback:true //不跳转,在开发单页应用时非常有用 react angular vue 都为单页面应用
            inline:true //实时刷新
            hot:true //使用热加载插件 HotModuleReplacementPlugin
        
        jsx 是react专有的文件格式

    1.9介绍webpack.production.config.js

        entry输入  //第三方依赖
            app
            vendor 
        output   
            path 
            filenname
            plugins  
                //定义为生产环境 编译react压缩到最小
                new webpack.DefinePlugin({
                    'process.env':{
                        'NODE_ENV':JSON.stringify(process.env.NODE_ENV)
                    }
                })
                // 分离CSS和JS文件
                new ExtractTextPlugin('/css/[name].[chunkhash:8].css')
                // 提供公共代码
                new webpack.optimize.CommonnsChunkPlugin({
                    name:'vendor',
                    filename:'/js/[name].[chunkhash:8].js'
                })
                // 可在业务 js 代码中使用 _DEV_ 判断是否是Dev模式
                new webpack.DefinePlugin({
                    __DEV__:JSON.stringify(JSON.parse((process.env.NODE_ENV == 'dev')))
                })


第二章 React基础

    2-1 介绍jsx

        (1)jsx react js和xml结合
        (2)import React from 'react'
        (3)import {render} from 'react-dom'
        // 定义组件
        class Hello extends React.Component{
            render(){
                return{
                    <p>hello</p>   
                }
            }
        }
        render(
            <Hello/>,
            document.getElementById('root')
        )

    2-2 jsx语法几点注意事项

        (1)使用一个父节点包裹
        (2)/* js注释 */   注释  {/*js注释*/}
            var m = 100 
            class Hello extends React.Component{
                render() {
                    return(
                        <div>
                            <p className="title">哈哈</p> 
                            <p style={{fontSize:'12px'}}>哈哈</p>   
                            <p>哈哈</p>   
                            <p>哈哈</p> 
                            <p>{m == 100?10:2}</p>  
                            {/*js注释*/}
                        </div> 
                    )
                }
            }     
        (3)class 为关键字  在写class 时候写成className 
        (4)style   
        
    2-3 jsx事件循环和判断

        class Hello extends React.Component{}
            //e 即js中的事件对象 例如 e.preventDefault 
            //函数执行时 this即组件本身 因为上面的 .bind(this)
            clickHandler(e){
                console.log(Date.now)
            }
            render() {
                var arr = ['aa','bb','cc']
                return(
                    <div>
                        <p onClick={this.clickHandler.bind(this)}>哈哈</p>   
                        <p style={{display:true?'block':'none'}}>哈哈</p> 
                        <ul>
                            {arr.map(function(item,index){
                                return <li key={index}>{item}</li>
                            })} 
                        </ul>  
                    </div> 
                )
            }
        }   
        

    2-4  代码分离方案

        index.js 里面做代码分离
        Hello - subpage 服务于 Hello下面的index.jsx
        component 组件层  比如header 头部比较通用 每个页面显示的内容不太一样,单独拉出来当做一个组件使用


    2-5  props和state
        父  <Header title="hello页面" aaa="aaa">
        子组件  <p>{this.props.title}-{this.props.aaa}</p> 
        
        constructor(props,context){
            supper(props,context)
            this.state = {
                now: Date.now()
            }
            clickHandler(){
                this.setState({
                    now:Date.now()
                })
            }
        }

    2-6  智能组件和木偶组件

        在react中 所有的单位都叫做组件,但是将它们分别放在了 container和conponents里面

        智能组件 
            在日常生活中 我们简称为页面 它只对数据负责,只需要获取了数据、定义好数据和操作的相关函数,然后将这些数据、函数直接传递给给具体实现的组件
        木偶组件 
            它从只能组件那里面接受到数据、函数、然后开始做一些展示工作,他的工作就是把拿到的数据展示给用户,函数操作开放给用户,至于数据内容是什么,函数操作是什么,不关心

    2.7  生命周期

        getInitialState 初始化组件 state 数据,但是在es6的语法中,我们可以使用以下书写方式代替

        class Hello extends React.Component {
            constructor(props,context){
                super(propsm,contnxt)
                this.state = {
                    now:Date.now()
                }
            }
            render(){
                return(

                )
            }
            componentDidMount(){
                //渲染完成 ajax
            }
            componentDidUpdate(prevProps,preState){
                //触发更新完成
            }
            componentWillUpdate(){

            }
        }

        render 
            最常用的hook,返回组件要渲染的模板
        componentDidMount
            组件第一次加载时渲染完成的时间,一般在此获取网络数据,实际开始项目时,会经常用到
        shouldComponentUpdate
            主要用于性能优化,React的性能优化也是一个很重要的话题
        componentDidUpdate
            组件更新了之后触发的时间,一般用于清空并更新数据,实际开始项目开发时 会经常用到

第三章 React优化方案和例子

    3-1 优化方案 

        介绍两种方式性能优化方式: 
        (1)性能检测 安装react性能检测工具 npm i react-addons-perf --save 
        然后在 ./app/index.jsx中
        import Perf form 'react-addons-perf' 
        if(__DEV__){ //开发环境下面才执行
            window.Perf = Perf 
        }
        
        浏览器里面console中 输入 
            Perf.start() 开始检测
            Perf.stop()  停止检测
            Perf.printWasted() 每个组件的运行时间

        (2)PureRenderMixin 优化
        React最基本的优化方式使用PureRenderMixin  安装工具 npm i react-addons-pure-render-mixin --save  然后在组件中引用使用
            
            import React from 'react'
            import PureRenderMixin from 'react-addons-puree-render-mixin'

            class List extends React.Component {
                constructor(props,context){
                    super(props,context)
                    this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this)
                }
            }

        react 有一个生命周期的hook 叫做shouldComponentUpdate,组件每次更新之前,都要过一遍这个函数,如果这个函数返回true则更新,如果返回false则不更新。而默认情况下,这个函数一直会返回true,就是说,如果有一些无效的改动触发了这个函数,也会导致无效的更新   


    3-2 todo-list-demo 

        todo.js

            import React from 'react'
            import Input from '../../components/Input'
            import List from '../../components/List'

            class Todo extends React.Component {
                constructor(props, context) {
                    super(props, context);
                    this.state = {
                        todos: []
                    }
                }
                render() {
                    return (
                        <div>
                        <Input submitFn={this.submitFn.bind(this)}/>
                        <List todos={this.state.todos} deleteFn={this.deleteFn.bind(this)}/>
                        </div>
                    )
                }
                submitFn(value) {
                    const id = this.state.todos.length
                    this.setState({
                        todos: this.state.todos.concat({
                            id: id,
                            text: value
                        })
                    })
                }
                deleteFn(id) {
                    let data = this.state.todos
                    this.setState({
                        todos: data.filter(item => {
                            if (item.id !== id) {
                                return item
                            }
                        })
                    })
                }
            }

            export default Todo


        input.jsx

            import React from 'react'
            class Input extends React.Component {
                constructor(props, context) {
                    super(props, context);
                    this.state = {
                        value: ''
                    }
                }
                render() {
                    return (
                        <div>
                        <input 
                                style={{width: '100%', height: '40px', fontSize: '35px'}}
                                value={this.state.value} 
                                onChange={this.changeHandler.bind(this)} 
                                onKeyUp={this.keyUpHandler.bind(this)}
                            />
                        </div>
                    )
                }
                changeHandler(e) {
                    // 实时同步数据
                    this.setState({value: e.target.value})
                }
                keyUpHandler(e) {
                    const value = this.state.value
                    if (e.keyCode === 13 && value.trim()) {
                        // 提交并清空数据
                        this.props.submitFn(value)
                        this.setState({value: ''})
                    }
                }
            }

            export default Input


        list.jsx

            import React from 'react'
            class List extends React.Component {
                render() {
                    const data = this.props.todos

                    return (
                        <ul style={{marginTop: '10px', fontSize: '20px', lineHeight: '30px'}}>
                            {data.map((item, index) => {
                                return <li key={index} onClick={this.clickHandler.bind(this, item.id)}>{item.text}</li>
                            })}
                        </ul>
                    )
                }
                clickHandler(id) {
                    this.props.deleteFn(id)
                }
            }

            export default List    


第四章 React Router

    4-1 跳转和参数

        npm install react-router --save 
        router 里面可以放入routerMap.jsx

            import React from 'react'
            import { Router, Route, IndexRoute } from 'react-router'

            import App from '../containers/App'
            import Home from '../containers/Home'
            import List from '../containers/List'
            import Detail from '../containers/Detail'
            import NotFound from '../containers/NotFound'

            class RouteMap extends React.Component {
                updateHandle() {
                    console.log('每次router变化之后都会触发')
                }
                render() {
                    return (
                        <Router history={this.props.history} onUpdate={this.updateHandle.bind(this)}>
                            <Route path='/' component={App}>
                                <IndexRoute component={Home}/>
                                <Route path='list' component={List}/>
                                <Route path='detail/:id' component={Detail}/>
                                <Route path="*" component={NotFound}/>
                            </Route>
                        </Router>
                    )
                }
            }

            export default RouteMap

        index.jsx 

            import React from 'react'
            import { render } from 'react-dom'
            import { hashHistory } from 'react-router'

            import RouteMap from './router/routeMap'

            render(
                <RouteMap history={hashHistory}/>,
                document.getElementById('root')
            )


        注意    hashHitory,规定用url中的hash来表示router 例如localhost:8080/#/list 
        与之对应的还有一个browserHistory也可用 它就不是用哪个hash 直接可以这样localhost:8080/list表示
        但是后者需要服务器端支持

    4-2 介绍router-map配置

        列表跳转详情页面 
        home.jsx
            import React from 'react'
            import { Link } from 'react-router'
            class Home extends React.Component {
                render() {
                    return (
                        <div>
                            <p>Home</p>
                            <Link to="/list">to list</Link>   
                        </div>
                    )
                }
            }

            export default Home

        list.jsx // 点击跳转

        import React from 'react'
        import { hashHistory } from 'react-router'

        class List extends React.Component {
            render() {
                const arr = [1, 2, 3]
                return (
                    <ul>
                        {arr.map((item, index) => {
                            return <li key={index} onClick={this.clickHandler.bind(this, item)}>js jump to {item}</li>
                        })}
                    </ul>
                )
            }
            clickHandler(value) {
                hashHistory.push('/detail/' + value)
            }
        }

        export default List

        列表页面跳转

        大型项目的静态资源懒加载问题  huge-pagee

第五章 React Redux 

    5-1 简单demo

        Redux 是一个数据状态管理插件 搭配React特别合适
        SPA程序,组件之间的共享信息是一个非常大的问题,用户登录之后客户端会存储用户信息(urserid,头像等),而系统的很多组件都会用到这些信息,例如收藏、点赞、评论等,这些组件在用到用户信息时候,Redux祈祷了作用

        安装 
            npm install redux --save 
            npm install react-redux --save 

        例子
            import { createStore } from 'redux'
            export default function(){
                // 定义计算规则 即reducer  第一步:定义计算规则,即 reducer
                function couter(state=0,action){
                    switch(action.type){
                        case 'INCREMENT':
                            return state + 1 
                        case 'DECREMENT':
                            return state - 1
                        default:
                            retrun state 
                    }
                }
            }
            // 根据计算规则生成 store createStore根据Redux来的  第二步:根据计算规则生成 store
            let store = createStore(counter)

            // 第三步:定义数据(即state变化之后的派发规则)
            store.subscribe(()=>{
                console.log('fn1=>current state',store.getState())
            })
            store.subscribe(()=>{
                console.log('fn2=>current state',store.getState())
            })

            // 触发数据变化 默认0  第四步:触发数据变化
            store.dispatch({type:'INCREMENT'}) //第一次 0+1 输出1
            store.dispatch({type:'INCREMENT'}) //第二次 1+1 输出2
            store.dispatch({type:'DECREMENT'}) //第三次 2-1 输出1

            


    5-2 结合react(1)

        总结:

        // 第一步:定义计算规则,即 reducer
        // 第二步:根据计算规则生成 store
        // 第三步:定义数据(即state变化之后的派发规则)
        // 第四步:触发数据变化

        React和Redux结合 

        一般文件内目录结构可以分为以下几个文件进行分类

        actions 
        components
        constants
        containers
        reducers
        router
        static
        store 
        utils


        选择城市的例子
        reducer文件夹里面有 index.js userinfo.js

        index.js

            import React from 'react'
            import userinfo from './userinfo'
            const rootReducer  = combineReducer({
                userinfo
            })

            export default rootReducer

        userinfo.js 

            import * as actionTypes from '../constants/userinfo' // 因为这个文件其他地方也要用 所以得引用

            const initialState = {}
            export default function userinfo(state = initialState,action){
                switch(action.type){
                    case actionTypes.USERINFO_LOGIN:
                        return action.data
                    case actionTypes.UPDATE_CITYNAME:
                        return action.data
                    default:
                        return state        
                }
            }

        actionTypes 

            export const USERINNFO_LOGIN = 'USERINNFO_LOGIN' 
            export const UPDATA_CITYNAME = 'UPDATA_CITYNAME' 



        store 文件夹里面configStore.js






    5-3 结合react(2)
        
        第二步 <Provider>包起来
        index.jsx  // 入口文件 所有组件用Provider包起来 本质上是react组件 针对redux做了一些封装

            import React from 'react'
            import { render } from 'react-dom'
            import { Provider } from 'react-redux'
            import configureStore from './store/configureStore'

            import Hello from './containers/Hello'

            const store  = configureStore()

            render(
                <Provier store={store}>
                    <Hello />
                </Provier>,
                document.getElementById('root')
            )

        Hello组件 Hello.jsx

            import React from 'react'
            import { connect } from 'react-redux'
            import { bindActionCreators } from 'redux'
            
            import * as userinfoActions from '../actions/userinfo'

            import A from '../components/A'
            import B from '../components/B'
            import C from '../components/C'

            class Hello extends React.Component{
                render() {
                    return (
                        <div>
                            <p>hello world</p>
                            <A userinfo={this.props.userinfo}></A>
                        </div>
                    )
                }
                componentDidMount(){
                    //模拟登陆
                    this.props.userinfoActions.login({
                        userid:'abc',
                        city:'beijing
                    })
                }
            }

            function mapStateToProps(state){
                return{
                    userinfo:state.userinfo
                }
            }

            function mapDispatchToProps(dispatch){
                return {
                    userinfoActions: bindActionnCreators(userinfoActions,dispatch)
                }
            }

            export default connect(
                mapStateToProps,
                mapDispatchToProps
            )(Hello)

        userinfo.js    // actions里面的userinfo

            import * as actionTypes form '../constants/userinfo'

            export function login(data){
                return {
                    type: actionTypes.USERINFO_LOGIN,
                    data
                }
            }
            export function updateCityeName(data){
                return {
                    type: actionTypes.UPDATE_CITYNAME,
                    data
                }
            }




    5-4 结合react-场景说明

        介绍 A B C 的三个组件 
        A 是userid 
        B 是city 
        C 修改按钮 点击的时候执行


第六章 fetch获取 提交数据,开发环境下的数据mock

    6-1 get请求

        ajax诟病 callback嵌套 promise 正式JS中解决这一问题的钥匙
        fetch就是一种可代替ajax获取提交数据的技术 有些高端浏览器已经可以使用window.fetch使用了,相对于使用jquery.ajax 它轻量 而且它支持Pro米斯特 更加符合现在的编程习惯

        安装

            npm install whatwg-fetch --save  
            npm install es6-promise --save  兼容老版本浏览器

        基本使用 // promise迷你书 图解http

            import 'whatwg-fetch'
            import 'es6-promise'


            var result = fetch('/api/123',{
                credentials:'include',
                headers:{
                    'Accept':'application/json, text/plain, */*'
                }
            })
            result.then(res =>{
                return res.text()
            }).then(text => {
                console.log(text)
            })


    6-2 post请求 

            import 'whatwg-fetch'
            import 'es6-promise'

            var result =  fetch('/api/post',{
                method:'POST',
                credentials:'include',
                header:{
                    'Accept':'application/json, text/plain, */*',
                    'Content-Type':'application/x-www-form-urlencoded'
                }, // 注意 post 时候 参数的形式
                body:'a=100&b=200'
            })    
            result.then(res =>{
                return res.json()
            }).then(json => {
                console.log(json)
            })

            前面的一部分可以分装起来  然后export出去  在引入

    6-3 数据模拟Mock

        一般分为三种方式:

            模拟静态数据 按照既定的数据格式 自己提供一些静态的JSON数据 如果 fis3 只用get方法场景,该项目不适用

            模拟动态接口 即自己使用一个web框架 按照既定的接口和数据结构的要求,自己模拟后端忌口的功能,让前端项目能顺利跑起来 该方式适用于新开发的项目 后端和前端同事开发
            
            转发线上接口 所有的接口直接代理获取线上的数据 post数据也都直接提交到线上 该项目适用于成熟项目中 而该项目是新开发的 没有线上接口 不适用

        安装    

            koa做后端的接口模拟  安装koa及其相关的插件
            npm install koa koa-body koa-router --save-dev   这里只有开发过程中使用 所以--save-dev


            package.json 中有关于mock的启动配置

            端口问题 端口 3000 和8080 

                webpack  中

                devServer:{
                    proxy:{
                        '/api':{
                            target:'http://localhost:3000',
                            secure:false
                        }
                    },
                    contentBase:"./public", //本地服务器所加载的页面所载的目录
                    colors:true, //终端中输出的结果为彩色
                    historyApiFallback:true, //不跳转
                    inline: true, //实时更新
                    hot: true  //使用热加载插件
                }


  第七章上  正式开发仿照大众点评

    7-1 首页 结构展示

        介绍结构  搜索栏 icon 广告  列表  
            
            需要知识 page   subpage  components 智能组件 木偶组件
            先做出一个head组件 并集成到页面中 css3
            接下来是轮播图react-swipe插件来将list做成轮播图
            接下来展示广告 subpage的使用
            接下来是列表 无线上拉加载更多
    
    7-2 准备工作-路由

        routerMap.jsx

            import React from 'react'
            import { Router, Route, IndexRoute } from 'react-router'

            import App from '../containers'
            import Home from '../containers/Home'
            import City from '../containers/City'
            import User from '../containers/User'
            import Search from '../containers/Search'
            import Detail from '../containers/Detail'
            import NotFound from '../containers/404'

            // 如果是大型项目,router部分就需要做更加复杂的配置
            // 参见 https://github.com/reactjs/react-router/tree/master/examples/huge-apps

            class RouterMap extends React.Component {
                render() {
                    return (
                        <Router history={this.props.history}>
                            <Route path='/' component={App}>
                                <IndexRoute component={Home}/>
                                <Route path='/city' component={City}/>
                                <Route path='/User' component={User}/>
                                <Route path='/search/:type(/:keyword)' component={Search}/>
                                <Route path='/detail/:id' component={Detail}/>
                                <Route path='*' component={NotFound}/>
                            </Route>
                        </Router>
                    )
                }
            }
        
    7-3 加载中展示
        
        // 加载中展示方式
        import React from 'react'
        import PureRenderMixin from 'react-addons-pure-render-mixin'

        class App extends React.Component {
            constructor(props, context) {
                super(props, context);
                this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
                this.state = {
                    initDone: false 
                }
            }
            componentDidMount(){
                // this 指向问题
                setTimeout(()=>{
                    that.setState({
                        initDone: true
                    })
                },1500)
            }
            render() {
                return (
                    <div>

                        {this.state.initDone?this.props.children:<div>加载中……</div>}
                    </div>
                )
            }
        }

        export default App

    7-4 获取城市 
        获取城市 => 然后获取城市后展示

        延伸 == 和 === 区别  
        == 进行类型的转换  === 严格相等  在写程序的时候 建议使用 === 

        util localStore.js 里面有相关方法  注意safari的无痕模式

        单独建立config 文件夹  里面放入 export const CITYNAME  = 'USER_CURRENT_CITY_NAME' 单独处理

        index.jsx 里面引入 然后获取

            import LocalStore from '../util/localStore'
            import { CITYNAME } from '../config/localStoreKey'
            let cityName = LocalStore.getItem(CITYNAME)

    7-5 城市信息存储到深圳 Redux

        index.jsx 

            import React from 'react'
            import PureRenderMixin from 'react-addons-pure-render-mixin'
            import LocalStore from '../util/localStore'
            import { CITYNAME } from '../config/localStoreKey'
            import { bindActionCreators } from 'redux'
            import { connect } from 'react-redux'
            import * as userInfoActionsForOtherFile from '../actions/userinfo'
            class App extends React.Component {
                constructor(props, context) {
                    super(props, context);
                    this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
                    this.state = {
                        initDone: false 
                    }
                }
                componentDidMount(){
                    //从localstorage里面获取城市
                    let cityName = LocalStore.getItem(CITYNAME)
                    if(cityName == null) {
                        cityName = '北京'
                    }
                    // 将城市信息存储到 Redux 中
                    this.props.userInfoActions.update({
                        cityName:cityName
                    })
                    setTimeout(()=>{ // 箭头函数 解决this问题
                        this.setState({
                            initDone: true
                        })
                    },1500)
                }
                render() {
                    return (
                        <div>
                            {this.state.initDone?this.props.children:<div>加载中……</div>}
                        </div>
                    )
                }
            }

            function mapStateToProps(state) {
                return{

                }
            }

            function mapDispatchToProps(dispatch) {
                return{
                userInfoActions: bindActionCreators(userInfoActionsForOtherFile,dispatch)
                }
            }

            export default connect(
                mapStateToProps,
                mapDispatchToProps
            )(App)

        userinfo.js // actions 里面的

            import * as actionTypes from '../constants/userinfo'

            export function update(data) {
                return {
                    type: actionTypes.USERINFO_UPDATE,
                    data
                }
            }

        userinfor.js // constants里面的

            export const USERINFO_UPDATE = 'USERINFO_UPDATE'


    7-6 Head布局

        木偶组件 => 涉及不到后端数据交互的
        智能组件 => 处理数据
        搭建Head组件

        import React from 'react'
        import PureRenderMixin from 'react-addons-pure-render-mixin'
        import './style.less'
        class HomeHeader extends React.Component {
            constructor(props, context) {
                super(props, context);
                this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
            }
            render() {
                return (
                    <div className="clear-fix">
                        <div className="float-left">
                            深圳
                            <i className="icon-angle-down"></i>
                        </div>
                    
                        <div className="float-right">
                            <i className="icon-user"></i>
                        </div>
                        <div>
                            <i className="icon-search"></i>
                            <input type="text"/>
                        </div>
                    </div>
                )
            }
        }

        module.exports = HomeHeader

        

    7-7 Header 图标

        iconmoon
        然后webpack中已经设置 
         static中css font.css
        目录下面创建fonts文件 放入文件
        然后在入口文件index.jsx中引入
       

    7-8 cityName显示

        传入    <HomeHeader cityName='西安'/>
        接收    <span>{this.props.cityName}</span>


    7-9 轮播图

        category文件夹

            新建category.js 文件和style.less文件
            小技巧 在一个文件下 如果想同时修改很多变量名字 command+D
            
            在home页面中引用

            轮播图使用react-swipe组件
            npm install swipe-js-iso whatwg-fetch --save
            引入
            import ReactSwipe from 'react-swipe'    
            render() {
                let reactSwipeEl;
                let opt = {
                    auto :2000
                }
                return (
                    <div>
                        <ReactSwipe
                            className="carousel"
                            swipeOptions={{ continuous: opt }}
                            ref={el => (reactSwipeEl = el)}
                        >
                            <div>PANE 1</div>
                            <div>PANE 2</div>
                            <div>PANE 3</div>
                        </ReactSwipe>
                    </div>
                )
            }


    7-10 展示索引值

        // 设置变量默认值为第一页  回调函数将当前的轮播值等于默认值  this处理 .bind(this) 
        constructor(props, context) {
            super(props, context);
            this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
            this.state = {
                index: 0
            }
        }
        render() {
            let reactSwipeEl;
            let opt = {
                auto: 2000,
                callback:function(index){
                    this.setState({index:index})
                }.bind(this)
            }
            return (
                <div>
                    <ReactSwipe
                        className="carousel"
                        swipeOptions={opt}
                        ref={el => (reactSwipeEl = el)}
                    >
                        <div>PANE 1</div>
                        <div>PANE 2</div>
                        <div>PANE 3</div>
                    </ReactSwipe>
                    <div>
                        {this.state.index}
                    </div>
                </div>
            )
        }



第七章下  正式开发仿照大众点评

    7-12 超值特惠  广告

        /api/homead 请求广告接口 返回广告的数据

        fetch-home-home.js
        mock.js 里面加入

            // 首页 —— 广告(超值特惠)
            var homeAdData = require('./home/ad.js')
            router.get('/api/homead', function *(next) {
                this.body = homeAdData
            });

            //这里面ad.js是引入的 所以需要列举出来
            ad.js是一个输出的数组 module.exports = []

    7-13  超值特惠 智能组件接收数据

        containers-Home-subpage 新建Ad.jsx 
        // 引入home.js的数据 默认给state一个值  componentDidMount中请求数据 如果有返回数据并且有数据 将本地的state值填充 setState 这个Ad.jsx中 是请求参数的 组件 所以这个是智能组件

        import React from 'react'
        import PureRenderMixin from 'react-addons-pure-render-mixin'
        import { getAdData } from '../../../fetch/home/home.js'

        class Ad extends React.Component {
            constructor(props, context) {
                super(props, context);
                this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
                this.state = {
                    data:[]
                }
            }
            render() {
                return (
                    <div>
                        {this.state.data.length}
                    </div>
                )
            }
            componentDidMount(){
                const result = getAdData()
                result.then((res) => {
                    return res.json()
                }).then((json)=>{
                    const data = json
                    if(data.length){
                        this.setState({
                            data:data
                        })
                    }
                })
            }
        }
        export default Ad
   
    7-14  超值特惠 木偶组件 展示数据 Ad.js引入数据

        import HomeAd from '../../../components/HomeAd/index'
        三目判断   
           <div>
                {
                    this.state.data.length
                    ? <HomeAd data={this.state.data}/>
                    : <div>{/* 加载中... */}</div>
                }
            </div>    

    7-15 mock数据

        containers-home-subpage  List.jsx

        import React from 'react'
        import PureRenderMixin from 'react-addons-pure-render-mixin'

        class List extends React.Component {
            constructor(props, context) {
                super(props, context);
                this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
            }
            render() {
                return (
                    <div>
                        list subpage
                    </div>
                )
            }
        }
        export default List


        mock文件夹里面 server.js
        // 首页 —— 推荐列表(猜你喜欢)
        var homeListData = require('./home/list.js')
        router.get('/api/homelist/:city/:page', function *(next) {
            // 参数
            const params = this.params
            const paramsCity = params.city
            const paramsPage = params.page

            console.log('当前城市:' + paramsCity)
            console.log('当前页数:' + paramsPage)

            this.body = homeListData
        });



    7-16 获取首页列表数据并展示 List.jsx  

        import React from 'react'
        import PureRenderMixin from 'react-addons-pure-render-mixin'
        import { getListData } from '../../../fetch/home/home'
        import ListCompoent from '../../../components/List'
        import LoadMore from '../../../components/LoadMore'

        import './style.less'  // 引入style.less 展示

        class List extends React.Component {
            constructor(props, context) {
                super(props, context);
                this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
                this.state = {
                    data: [],
                    hasMore: false,
                    isLoadingMore: false,
                    page: 0
                }
            }
            render() {
                return (
                    <div>
                        <h2 className="home-list-title">猜你喜欢</h2>
                        {
                            this.state.data.length
                            ? <ListCompoent data={this.state.data}/>
                            : <div>{/* 加载中... */}</div>
                        }
                        {
                            this.state.hasMore
                            ? <LoadMore isLoadingMore={this.state.isLoadingMore} loadMoreFn={this.loadMoreData.bind(this)}/>
                            : ''
                        }
                    </div>
                )
            }
            componentDidMount() {
                // 获取首页数据
                this.loadFirstPageData()
            }
            // 获取首页数据
            loadFirstPageData() {
                const cityName = this.props.cityName 
                const result = getListData(cityName, 0) // 刚开始请求数据为0
                this.resultHandle(result) // 单独函数 传入请求
            }
            // 加载更多数据
            loadMoreData() {
                // 记录状态
                this.setState({
                    isLoadingMore: true
                })

                const cityName = this.props.cityName
                const page = this.state.page
                const result = getListData(cityName, page)
                this.resultHandle(result)

                // 增加 page 技术
                this.setState({
                    page: page + 1,
                    isLoadingMore: false
                })
            }
            // 处理数据
            resultHandle(result) {   // 
                result.then(res => {
                    return res.json()
                }).then(json => {
                    const hasMore = json.hasMore
                    const data = json.data

                    this.setState({
                        hasMore: hasMore,
                        // 注意,这里讲最新获取的数据,拼接到原数据之后,使用 concat 函数
                        data: this.state.data.concat(data)
                    })
                }).catch(ex => {
                    if (__DEV__) {
                        console.error('首页”猜你喜欢“获取数据报错, ', ex.message)
                    }
                })
            }
        }

        export default List

    7-17 列表

        图片 标题 描述 价格  销售量
        components/List/Item/index.jsx  index.jsx
        
        import React from 'react'
        import PureRenderMixin from 'react-addons-pure-render-mixin'
        import './style.less'

        class ListItem extends React.Component {
            constructor(props, context) {
                super(props, context);
                this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
            }
            render() {
                const data = this.props.data
                return (
                    <div className="list-item clear-fix">
                        <div className="item-img-container float-left">
                            <img src={data.img} alt={data.title}/>
                        </div>
                        <div className="item-content">
                            <div className="item-title-container clear-fix">
                                <h3 className="float-left">{data.title}</h3>
                                <span className="float-right">{data.distance}</span>
                            </div>
                            <p className="item-sub-title">
                                {data.subTitle}
                            </p>
                            <div className="item-price-container clear-fix">
                                <span className="price float-left">¥{data.price}</span>
                                <span className="mumber float-right">已售{data.mumber}</span>
                            </div>
                        </div>
                    </div>
                )
            }
        }

        export default ListItem


        components/List/index.jsx   //引入Item组件单独处理

        import React from 'react'
        import PureRenderMixin from 'react-addons-pure-render-mixin'
        import Item from './Item'
        import './style.less'

        class List extends React.Component {
            constructor(props, context) {
                super(props, context);
                this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
            }
            render() {
                return (
                    <div className="list-container">
                        {this.props.data.map((item, index) => {
                            return <Item key={index} data={item}/>
                        })}
                    </div>
                )
            }
        }

        export default List
    
    7-18 Item组件实现

        搭建猜你喜欢的样式布局
        注意效率问题
        列表的搭建

    7-19 加载更多的实现

        首先应该准备3个状态
        this.state={
            data:[],      // 存储列表信息
            hasMore:false // 记录当前状态下还有没有更多的数据可供加载
            isLoadingMore:false, // 记录当前状态下,是加载中 还是点击加载更多
            page:1
        }

        创建LoadMore组件

        根绝this.state.hasMore 判断
        如果有的话显示     组件 <LoadMore isLoadingMore={this.state.isLoadingMore} loadMoreFn={this.loadMoreData.bind(this)}/>
        如果没有的话不显示

        componentDidMount() {     // 获取首页数据
            this.loadFirstPageData()
        }
        // 获取首页数据
        loadFirstPageData() {
            const cityName = this.props.cityName
            const result = getListData(cityName, 0)
            this.resultHandle(result)
        }
        // 加载更多数据
        loadMoreData() {
            // 记录状态
            this.setState({
                isLoadingMore: true
            })
            const cityName = this.props.cityName
            const page = this.state.page
            const result = getListData(cityName, page)
            this.resultHandle(result)
            // 增加 page 技术
            this.setState({
                page: page + 1,
                isLoadingMore: false
            })
        }
         // 处理数据
        resultHandle(result) {
            result.then(res => {
                return res.json()
            }).then(json => {
                const hasMore = json.hasMore
                const data = json.data
                this.setState({
                    hasMore: hasMore,
                    // 注意,这里讲最新获取的数据,拼接到原数据之后,使用 concat 函数
                    data: this.state.data.concat(data)
                })
            }).catch(ex => {
                if (__DEV__) {
                    console.error('首页”猜你喜欢“获取数据报错, ', ex.message)
                }
            })
        }


    7-20 LoadMore组件

        component/LoadMore/ 下面新建index.jsx 和style.less
        完善 List.jsx 的loadMoreData方法
        
        loadMoreData() {
            // 记录状态 点击改变状态 变成加载中
            this.setState({
                isLoadingMore: true
            })
            const cityName = this.props.cityName
            const page = this.state.page
            const result = getListData(cityName, page)
            this.resultHandle(result)
            // 增加 page 技术
            this.setState({
                page: page + 1,
                isLoadingMore: false
            })
        }

        LoadMore组件

            render() {
                return (
                    <div className="load-more" ref="wrapper">
                        {
                            this.props.isLoadingMore
                            ? <span>加载中...</span>
                            : <span onClick={this.loadMoreHandle.bind(this)}>加载更多</span>
                        }
                    </div>
                )
            }
            loadMoreHandle() {
                // 执行传输过来的
                this.props.loadMoreFn();
            }
    

    7-21 LoadMore自动叠加方式
        
        index.jsx

        componentDidMount() {
            // 使用滚动时自动加载更多
            const loadMoreFn = this.props.loadMoreFn
            const wrapper = this.refs.wrapper
            let timeoutId
            function callback() {
                console.log(456)
                const top = wrapper.getBoundingClientRect().top
                const windowHeight = window.screen.height
                if (top && top < windowHeight) {
                    // 证明 wrapper 已经被滚动到暴露在页面可视范围之内了
                    loadMoreFn()
                }
            }
            window.addEventListener('scroll', function () {
                if (this.props.isLoadingMore) {
                    return
                }
                console.log(123)
                if (timeoutId) {
                    clearTimeout(timeoutId)
                }
                timeoutId = setTimeout(callback, 50)
            }.bind(this), false);
        }

        // addEventListener 监听scroll事件 如果isLoadingMore有 说明正在加载 跳出
        做一个截流 如果timeoutId有 清空定时器  如果没有的话 执行定时器
        可以分别打印一下 可以发现只有在拉动页面的时候才会触发 打印456

        const wrapper = this.refs.wrapper  // 拿到dom
        const top = wrapper.getBoundingClientRect().top //距离顶部的距离
        const windowHeight = window.screen.height  // 拿到window的高度
        if (top && top < windowHeight) {
            // 证明 wrapper 已经被滚动到暴露在页面可视范围之内了
            loadMoreFn()
        }
        // 当有距离 而且距离小于屏幕高度   当加载更多漏出来 立马加载更多

第八章上 开发城市页面

    8-1 路由页面
        
        HomeHeader里面的index.jsx文件 跳转方式
        <Link to="/city">
            <span>{this.props.cityName}</span>
            &nbsp;
        </Link>

        点击城市 跳转到城市页面 
        城市页面功能 1.显示当前城市 2.语序修改城市
        两个功能需要Redux支持  可以在component里面先打印出两个页面的值 
        city需要连接Redux 引入并应用


        function mapStateToProps(state) {
            return {
                userinfo:state.userinfo
            }
        }

        function mapDispatchToProps(dispatch) {
            return {
                userInfoActions: bindActionCreators(userInfoActionsFromOtherFile, dispatch),
            }
        }
        export default connect(
            mapStateToProps,
            mapDispatchToProps
        )(City)

    8-2 跳转链接

        标题栏可以抽离出来做一个组件  新建component/Header/index.jsx style.less

        做成React组件代码结构  然后在City/index.jsx中里面引用

        import React from 'react'
        import PureRenderMixin from 'react-addons-pure-render-mixin'
        import { bindActionCreators } from 'redux'
        import { connect } from 'react-redux'
        import * as userInfoActionsFromOtherFile from '../../actions/userinfo' 

        class Header extends React.Component {
            constructor(props, context) {
                super(props, context);
                this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
            }
            render() {
                return (
                    <div>
                        <h1>Header</h1>
                    </div>
                )
            }
        }
        export default Header

    8-3-4-5 city页面的处理

        Header页面需要做的两个功能
        1.city页面做传值处理  {this.props.title}
        2.返回    
            <span onClck={this.clickHandle}>返回</span> 
            clickHandle(){
                window.history.back()
            }
        然后布局+样式 

        当前城市的显示 单独做一个组件CurrentCity组件 直接显示 细分组件 为了应对以后的需求
        热门城市列表 单租也做一个组件CityList城市固定写死

        两个组件分别在components里面建立对应文件夹 然后引入各自的style.less 然后在City组件里面引入


    8-6 城市的列表点击选择

        首先样式布局,然后执行changeCity方法
        changeCity方法首先在city页面里面 
        
        //更改程城市列表点击的时候获取点击的城市 如果没有城市则return 如果有
        第一步修改Redux将点击的城市名称赋值到当前变量userinfo 值上面 
        第二步执行Redux的更新方法 更新userinfo 然后修改localStore 引入相关的LocalStore和CITYNAME 进行本地存储的更改 
        第三步修改完毕跳转首页
        
        将此方法传入到子组件中<CityList changeFn={this.changeCity.bind(this)} />
    

        changeCity(newCity){
            console.log(newCity)
            if(newCity === null ){
                return 
            }
            // 修改Redux
            const userinfo = this.props.userinfo
            userinfo.cityName = newCity
            this.props.userInfoActions.update(userinfo)

            // 修改LocalStore
            LocalStore.setItem(CITYNAME,newCity)

            //跳转首页
            hashHistory.push('/')
        }



第九章开发搜索结果页面

    9-1 首页进入搜索页面
        两种方式 输入框和icon

        通过react-router引入Link 
        <Link to="/search/jingdian"><li className="float-left jingdian">景点</li></Link>

        需要注意的是 这种方式跳转到搜索结果页的路由是怎样的
        



    9-2 约束性和非约束性组件

        <input type="text" defaultValue="a" ref="input">
        this.refs.input 
        console.log(input.value)

        依赖DOM操作 不符合组件化的设计 也不易扩展
        查询DOM消耗更多性能

    约束性 
        监控input的变化将值实时存到state中,直接从state中获取
        Reavt或者Vue是一种基于数据驱动视图的设计方式,定好数据和视图的规则之后,只更改数据,不直接操作DOM



        HomeHeader index.jsx

        <input type="text" placeholder="请输入关键字"
                        onChange={this.HandleChange.bind(this)}
                        onKeyUp={this.KeyUpHandle.bind(this)}
                        value={this.state.kwd}/>
        KeyUpHandle(e){
            if(e.keyCode !== 13){
                return
            }
            hashHistory.push('/search/all'+ encodeURIComponent(this.state.kwd))
        }
        HandleChange(e){
            this.setState({
                kwd:e.target.value
            })
        }

    9-3 抽离input

        两个头部都要用到search组件 所以抽离
        compoment 里面建立 SearchInput 组件 
        searchinput

        class SearchInput extends React.Component {
            constructor(props, context) {
                super(props, context);
                this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
                this.state={
                    value:''
                }
            }
            render() {
                return (
                    <input type="text" placeholder="请输入关键字"
                                    onChange={this.HandleChange.bind(this)}
                                    onKeyUp={this.KeyUpHandle.bind(this)}
                                    value={this.state.value}/>
                )
            }
            componentDidMount(){
                this.setState({
                    value:this.props.value || ''
                })
            }
            KeyUpHandle(e){
                if(e.keyCode !== 13){
                    return
                }
                this.props.enterHandle(e.target.value)
            }
            HandleChange(e){
                this.setState({
                    value:e.target.value
                })
            }
        }


    9-4 抽离input

        <SearchInput value="abc" enterHandle={this.enterHandle.bind(this)} />
        enterHandle(value) {
            hashHistory.push('/search/all/' + encodeURIComponent(value))
        }




    9-5 searcHeader组件 

        component里面新建SearchHeader文件夹
        返回的按钮和输入框 可以直接引用

        在search index.jsx页面里面 获取url参数 this.props.params 
        然后传到子组件  <SearchHeader keyword={params}/>

        在SearchHeader index.jsx页面里面 获取

        返回事件 onClick={this.clickHandle.bind(this)}

        clickHandle(value){
            window.history.back()
        }

        然后布局样式

    然后关于这页面的数据 是采用接口请求的 如下

        第一步编写SearchList组件 然后 引入相关组件
        subpage/List.jsx  

        引入 getSearchData 数据 fetch 页面中新建search.js 写接口

    mock/search/list 为list模拟数据
    server.js为 路由请求



第十章 开发详情

    10-1 详情页面结构

        header 信息页面  点评页面(上拉加载更多)
        Detail 页面
        component/List/Item 下面 引入Link 然后嵌套 <Link to={'/detail/' + data.id}></Link>
        
        containers/Detail/index.jsx 引入Header信息

        import Header from '../../components/Header/index'
        <Header title="商户详情"/>

    10-2  商户信息模块

        subpage
        1.补充 mock/home/list.js 里面id的内容
        2.mock新增detail文件夹 新增文件 info.js comment.js
        3.在mock/server 文件夹里面新增接口
        4.fetch/detail/detail 里面新增接口
            export function getInfoData(id){
                const result = get('/api/detail/info/' +id)
                return result;
            }

            export function getCommentData(page, id) {
                const result = get('/api/detail/comment/' + page + '/' +id)
                return result 
            }
        

        判断一个对象是否总是true 
        var  a = false 
        var  b = {}
        !!a   // false
        !!b   // true

    10-3 detailInfo组件

        detailIInfo页面 info.jsx页面中引入并传值
        <DetailInfo data={ this.state.info }/>
        <p dangerouslySetInnerHTML={{__html:data.desc}}></p>
        dangerouslySetInnerHTML
        xss攻击  在一些上传文件中如果写script 
        <script>document.get</script> 
        如果直接<p>{data.desc}</p> 没有问题 尖括号不识别

        如果 <p dangerouslySetInnerHTML={{__html:data.desc}}></p>方式写。就会有问题。所以加上dangerouslySetInnerHTML进行处理

    10-4 star组件
        
        render(){
            // 获取 star 数量,并取余5(最多5个star)
            let star = this.props.star || 0 
            if(star>5){
                star = star % 5
            }
            return(
                <div className="star-container">
                    {
                        [1,2,3,4,5].map((item,index)=>{
                            const lightClass = star >= item ? ' light' : ''
                            return <i key={index} className={'icon-star' + lightClass}></i>
                        })
                    }
                </div>
            )
        }

    10-5 用户评论列表 comment.jsx

        传递id 布局页面     
        列表循环遍历 然后列出电话 评分 一级描述信息 
        和列表页面大致相同 
        
        constructor(props,context){
            super(props,context)
            this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
            this.state = {
                data: [],
                hasMore: false,
                isLoadingMore: false,
                page: 0
            }
        }
        render(){
            const _id = this.props.id
            return(
                <div className="detail-comment-subpage">
                    <h2>用户点评</h2>
                    {
                        this.state.data.length
                        ? <ListComponent data={this.state.data}/>
                        : <div>{/* 加载中... */}</div>
                    }
                    {
                        this.state.hasMore
                        ? <LoadMore isLoadingMore={this.state.isLoadingMore} loadMoreFn={this.loadMoreData.bind(this)}/>
                        : ''
                    }
                </div>
            )
        }
        componentDidMount() {
            this.loadFirstPageData();
        }
        // 获取首页数据
        loadFirstPageData() {
            const id = this.props.id
            const result = getCommentData(0, id)
            this.resultHandle(result)
        }
        // 加载更多数据
        loadMoreData() {
            // 记录状态
            this.setState({
                isLoadingMore: true
            })
            const id = this.props.id
            const page = this.state.page
            const result = getCommentData(page, id)
            this.resultHandle(result)

            // 增加 page 技术
            this.setState({
                isLoadingMore: false
            })
        }
        // 处理数据
        resultHandle(result) {
            result.then(res => {
                return res.json()
            }).then(json => {
                // 增加 page 技术
                const page = this.state.page
                this.setState({
                    page: page + 1
                })
                const hasMore = json.hasMore
                const data = json.data
                this.setState({
                    hasMore: hasMore,
                    // 注意,这里讲最新获取的数据,拼接到原数据之后,使用 concat 函数
                    data: this.state.data.concat(data)
                })
            }).catch(ex => {
                if (__DEV__) {
                    console.error('详情页获取用户评论数据出错, ', ex.message)
                }
            })
        }


第十一章 登录页面 

    class Login extends React.Component {
        constructor(props, context) {
            super(props, context);
            this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
            this.state = {
                checking: true
            }
        }
        render() {
            return (
                <div>
                    <Header title="登录"/>
                    {
                        // 等待验证之后,再显示登录信息
                        this.state.checking
                        ? <div>{/* 等待中 */}</div>
                        : <LoginComponent loginHandle={this.loginHandle.bind(this)}/>
                    }
                </div>
            )
        }
        componentDidMount() {
            // 判断是否已经登录
            this.doCheck()
        }
        doCheck() {
            const userinfo = this.props.userinfo
            if (userinfo.username) {
                // 已经登录,则跳转到用户主页
                this.goUserPage();
            } else {
                // 未登录,则验证结束
                this.setState({
                    checking: false
                })
            }
        }
        // 处理登录之后的事情
        loginHandle(username) {
            // 保存用户名
            const actions = this.props.userInfoActions
            let userinfo = this.props.userinfo
            userinfo.username = username
            actions.update(userinfo)

            const params = this.props.params
            const router = params.router
            if (router) {
                // 跳转到指定的页面
                hashHistory.push(router)
            } else {
                // 跳转到用户主页
                this.goUserPage()
            }
        }
        goUserPage() {
            hashHistory.push('/User')
        }
    }


第12章 

    12-1 购买收藏按钮 buyAndStore

        创建组件并且引入 然后购买和存储按钮分别建立各自时间

    12-2 验证登录

        首先进行验证登录的处理 先看一下id
        loginCheck(){
            const id = this.props.id
            const userinfo = this.props.userinfo
            if(!userinfo.username){
                hashHistory.push('Login/'+ encodeURICompomemt('/detail/' + id))
                return false
            }
        }

    12-3 12-4 12-5

        配置store 

第13章  用户主页
    13-1    

        处理一下header 组件  判断如果 有backRouter属性 则跳转相应 如果没有则返回上一级

        import { hashHistory } from 'react-router'
        clickHandle(){
            const backRouter = this.props.backRouter
            if(backRouter){
                hashHistory.push(backRouter)
            }else{
                window.history.back()
            }
        }
    13-2  userinfo组件

    13-3 购买列表 orderList列表
        render() {
            const data = this.props.data
            return (
                <div>
                    {data.map((item, index) => {
                        return <Item key={index} data={item}/>
                    })}
                </div>
            )
        }
    13-4 Item 组件 列表循环
     
第14章  订单评价

    14-1 评价状态
        1.未评价 评价按钮可点 状态值0 
        2.评价中 状态值1
        3.已评价 状态值2
        单独有个评价组件  根据状态 然后改变相应的状态
        评价来源
    14-2  代码实现


    14-3

    14-4
    

About

React实现大众点评webApp

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published