跟我一起做一个vue的小项目(去哪儿网APPvue2.5完结篇)

先放一下这个完结项目的整体效果
跟我一起做一个vue的小项目(去哪儿网APPvue2.5完结篇)
下面跟我我一起进行下面项目的进行吧~~~
接下来我们进行的是实现header的渐隐渐显效果,并且点击返回要回到首页
我们先看效果
跟我一起做一个vue的小项目(去哪儿网APPvue2.5完结篇)
在处理详情页向下移动过程中,header页出现,我们使用的是监听滚动的方法,然后动态传入样式

//src\pages\detail\components\Header.vue
<template>
    <div>
        <router-link
            class="header-abs"
            tag="div"
            to="/"
            v-show="showAbs"
        >
            <div class="iconfont header-abs-back">
                &#xe624;
            </div>
        </router-link>
        <div
            class="header-fixed"
            v-show="!showAbs"
            :style="opacityStyle"
        >
            <router-link to="/">
                <div class="iconfont header-icon-back">&#xe624;</div>
            </router-link>
            景点详情
        </div>
    </div>
</template>
<script>
export default {
  name: 'DetailHeader',
  data () {
    return {
      showAbs: true,
      opacityStyle: {
        opacity: 0
      }
    }
  },
  methods: {
    handleScroll () {
      console.log('滾動艦艇', document.documentElement.scrollTop)
      const top = document.documentElement.scrollTop
      if (top > 60) {
        let opacity = top / 140
        opacity = opacity > 1 ? 1 : opacity
        this.opacityStyle = {
          opacity
        }
        this.showAbs = false
      } else {
        this.showAbs = true
      }
    }
  },
  activated () {
    window.addEventListener('scroll', this.handleScroll)
  }
}
</script>
<style lang="stylus" scoped>
@import '~styles/varibles.styl';
.header-abs
    position:absolute
    left:.2rem
    top:.2rem
    width:.8rem
    height:.8rem
    border-radius:.4rem
    background:rgba(0,0,0,0.8)
    text-align:center
    line-height:.8rem
    .header-abs-back
        color:#fff
        font-size:.4rem
.header-fixed
    height :$headerHeight
    line-height:$headerHeight
    overflow:hidden
    position:fixed
    top:0
    left:0
    right:0
    text-align:center
    color:#fff
    background:$bgColor
    font-size:.32rem
    .header-icon-back
        width:.64rem
        text-align:center
        font-size:.4rem
        position:absolute
        top:0
        left:0
        color:#fff
</style>
//src\pages\detail\Detail.vue
<template>
    <div>

        <detail-banner></detail-banner>
         <detail-header></detail-header>
         <div class="container"></div>
    </div>
</template>
<script>
import DetailBanner from './components/Banner'
import DetailHeader from './components/Header'
export default {
  name: 'Detail',
  components: {
    DetailBanner,
    DetailHeader
  }
}
</script>
<style lang="stylus" scoped>
.container
  height:50rem
</style>

接下来我们对项目中,全局事件进行解绑
当我们在header.vue组件中使用 window.addEventListener(‘scroll‘, this.handleScroll)的时候,
我们会发现,在首页中滚动页面也会调用这个事件
跟我一起做一个vue的小项目(去哪儿网APPvue2.5完结篇)
我们使用keep-alive在缓存过程中产生的钩子函数进行解决这个问题
当在这些组件之间切换的时候都会请求一些请求过的数据,每次请求都会导致重复渲染影响性能。这些数据可以存到缓存。此时使用 activate:是在被包裹组件被激活的状态下使用的生命周期钩子,deactivated:在被包裹组件停止使用时调用
我们的header组件中添加
跟我一起做一个vue的小项目(去哪儿网APPvue2.5完结篇)
这样就解决了在首页中,也会进行滚动事件的Bug

//header.vue
<template>
    <div>
        <router-link
            class="header-abs"
            tag="div"
            to="/"
            v-show="showAbs"
        >
            <div class="iconfont header-abs-back">
                &#xe624;
            </div>
        </router-link>
        <div
            class="header-fixed"
            v-show="!showAbs"
            :style="opacityStyle"
        >
            <router-link to="/">
                <div class="iconfont header-icon-back">&#xe624;</div>
            </router-link>
            景点详情
        </div>
    </div>
</template>
<script>
export default {
  name: 'DetailHeader',
  data () {
    return {
      showAbs: true,
      opacityStyle: {
        opacity: 0
      }
    }
  },
  methods: {
    handleScroll () {
      console.log('滾動艦艇', document.documentElement.scrollTop)
      const top = document.documentElement.scrollTop
      if (top > 60) {
        let opacity = top / 140
        opacity = opacity > 1 ? 1 : opacity
        this.opacityStyle = {
          opacity
        }
        this.showAbs = false
      } else {
        this.showAbs = true
      }
    }
  },
  activated () {
    window.addEventListener('scroll', this.handleScroll)
  },
  //   移除全局事件的影响
  dactivated () {
    window.removeEventListener('scroll', this.handleScroll)
  }
}
</script>
<style lang="stylus" scoped>
@import '~styles/varibles.styl';
.header-abs
    position:absolute
    left:.2rem
    top:.2rem
    width:.8rem
    height:.8rem
    border-radius:.4rem
    background:rgba(0,0,0,0.8)
    text-align:center
    line-height:.8rem
    .header-abs-back
        color:#fff
        font-size:.4rem
.header-fixed
    height :$headerHeight
    line-height:$headerHeight
    overflow:hidden
    position:fixed
    top:0
    left:0
    right:0
    text-align:center
    color:#fff
    background:$bgColor
    font-size:.32rem
    .header-icon-back
        width:.64rem
        text-align:center
        font-size:.4rem
        position:absolute
        top:0
        left:0
        color:#fff
</style>

接下来我们使用递归组件实现详情页的列表
所谓递归组件是什么?就是在组件里面调用自身。
我们先放一下实现的效果图
跟我一起做一个vue的小项目(去哪儿网APPvue2.5完结篇)
先在detail页面中定义好数据格式,并将数据传递给子组件

//list.vue
<template>
    <div>

        <detail-banner></detail-banner>
         <detail-header></detail-header>
         <div class="container">
           <detail-list :list="list"></detail-list>
         </div>
    </div>
</template>
<script>
import DetailBanner from './components/Banner'
import DetailHeader from './components/Header'
import DetailList from './components/List'
export default {
  name: 'Detail',
  components: {
    DetailBanner,
    DetailHeader,
    DetailList
  },
  data () {
    return {
      list: [
        {
          title: '成人票',
          children: [
            {
              title: '成人一馆联票',
              children: [
                {
                  title: '成人一馆联票-宝安连锁店销售'
                },
                {
                  title: '成人一馆联票-龙岗连锁店销售'
                },
                {
                  title: '成人一馆联票-坂田连锁店销售'
                }
              ]
            },
            {
              title: '成人二馆联票'
            },
            {
              title: '成人三馆联票'
            }
          ]
        },
        {
          title: '学生票',
          children: [
            {
              title: '学生一馆联票'
            },
            {
              title: '学生二馆联票'
            },
            {
              title: '学生三馆联票'
            }
          ]
        },
        {
          title: '儿童票',
          children: [
            {
              title: '儿童一馆联票',
              children: [
                {
                  title: '儿童一馆联票-宝安连锁店销售'
                },
                {
                  title: '儿童一馆联票-龙岗连锁店销售'
                },
                {
                  title: '儿童一馆联票-坂田连锁店销售'
                }
              ]
            },
            {
              title: '儿童二馆联票'
            },
            {
              title: '儿童三馆联票'
            }
          ]
        }
      ]
    }
  }
}
</script>
<style lang="stylus" scoped>
// .container
//   height:50rem
</style>
//list.vue
//src\pages\detail\components\List.vue
<template>
<div>
    <div class="item" v-for="(item,index) of list" :key="index">
        <!-- 渲染最外层数据 -->
        <div class="item-title border-bottom">
            <span class="item-title-icon "></span>
            {{item.title}}
        </div>
        <!-- 如果最外层数据有子数据,就渲染子数据,递归调用本身 -->
        <div v-if="item.children" class="item-children">
            <detail-list :list="item.children"></detail-list>
        </div>
    </div>
</div>
</template>
<script>

export default {
  name: 'DetailList',
  props: {
    list: Array
  }
}
</script>
<style lang="stylus" scoped>
.item-title-icon
    display: inline-block;
    width: .36rem;
    height: .36rem;
    background: url(http://s.qunarzz.com/piao/image/touch/sight/detail.png) 0 -.45rem no-repeat;
    margin-right: .1rem;
    background-size: .4rem 3rem;
    vertical-align: middle;
.item-title
    line-height:0.8rem
    font-size:.32rem
    padding:0 .2rem
.item-children
    padding-left:0.6rem
</style>

接下来我们使用ajax来动态渲染数据
我们在mock数据引入detail.json

{
    "ret": true,
    "data": {
      "sightName": "大连圣亚海洋世界(AAAA景区)",
      "bannerImg": "http://img1.qunarzz.com/sight/p0/201404/23/04b92c99462687fa1ba45c1b5ba4ad77.jpg_600x330_bf9c4904.jpg",
      "commentsNum": 27,
      "gallaryImgs": [
        "http://img1.qunarzz.com/sight/p0/201404/23/04b92c99462687fa1ba45c1b5ba4ad77.jpg_800x800_70debc93.jpg",
        "http://img1.qunarzz.com/sight/p0/1709/76/7691528bc7d7ad3ca3.img.png_800x800_9ef05ee7.png"
      ],
      "categoryList": [
        {
          "title": "成人票",
          "children": [
            {
              "title": "成人三馆联票",
              "children": [
                {
                  "title": "成人三馆联票 - 某一连锁店销售"
                }
              ]
            },
            {
              "title": "成人五馆联票"
            }
          ]
        },
        {
          "title": "学生票"
        },
        {
          "title": "儿童票"
        },
        {
          "title": "特惠票"
        }
      ]
    }
  }

在detail中引入数据

//src\pages\detail\Detail.vue
<template>
    <div>

        <detail-banner
          :sightName="sightName"
          :bannerImg="bannerImg"
          :bannerImgs="gallaryImgs"
        ></detail-banner>
         <detail-header ></detail-header>
         <div class="container">
           <detail-list :list="list"></detail-list>
         </div>
    </div>
</template>
<script>
import DetailBanner from './components/Banner'
import DetailHeader from './components/Header'
import DetailList from './components/List'
import axios from 'axios'
export default {
  name: 'Detail',
  components: {
    DetailBanner,
    DetailHeader,
    DetailList
  },
  data () {
    return {
      sightName: '',
      bannerImg: '',
      gallaryImgs: [],
      list: [
      ]
    }
  },
  methods: {
    getDetailInfo () {
      axios.get('/api/detail.json?id=' + this.$route.params, {
        params: {
          id: this.$route.params.id
        }
      }).then(this.handleGetDataSucc)
    },
    handleGetDataSucc (res) {
      console.log('res', res)
      res = res.data
      if (res.ret && res.data) {
        const data = res.data
        console.log('data', data)
        this.sightName = data.sightName
        this.bannerImg = data.bannerImg
        this.gallaryImgs = data.gallaryImgs
        this.list = data.categoryList
      }
    }
  },
  mounted () {
    this.getDetailInfo()
  }
}
</script>
<style lang="stylus" scoped>
// .container
//   height:50rem
</style>

将detail中传入的数据,传递给子组件

//src\pages\detail\components\List.vue
<template>
<div>
    <div class="item" v-for="(item,index) of list" :key="index">
        <!-- 渲染最外层数据 -->
        <div class="item-title border-bottom">
            <span class="item-title-icon "></span>
            {{item.title}}
        </div>
        <!-- 如果最外层数据有子数据,就渲染子数据,递归调用本身 -->
        <div v-if="item.children" class="item-children">
            <detail-list :list="item.children"></detail-list>
        </div>
    </div>
</div>
</template>
<script>

export default {
  name: 'DetailList',
  props: {
    list: Array
  }
}
</script>
<style lang="stylus" scoped>
.item-title-icon
    display: inline-block;
    width: .36rem;
    height: .36rem;
    background: url(http://s.qunarzz.com/piao/image/touch/sight/detail.png) 0 -.45rem no-repeat;
    margin-right: .1rem;
    background-size: .4rem 3rem;
    vertical-align: middle;
.item-title
    line-height:0.8rem
    font-size:.32rem
    padding:0 .2rem
.item-children
    padding-left:0.6rem
</style>
//src\pages\detail\components\Banner.vue
<template>
    <div>
      <div class="banner" @click="handleBannerClick">
          <img :src="bannerImg" alt="" class="banner-img">
          <div class="banner-info">
            <div class="banner-title">
             {{this.sightName}}
            </div>
            <div class="banner-number">
              <span class="iconfont">&#xe758;</span>
              {{this.bannerImgs.length}}
            </div>
        </div>
      </div>
      <common-gallary
        :imgs="bannerImgs"
        v-show="showGallery"
        @close="handlegalleryClose"
      ></common-gallary>
    </div>
</template>
<script>
import CommonGallary from 'common/gallary/Gallary'
export default {
  name: 'DetailBanner',
  props: {
    sightName: String,
    bannerImg: String,
    bannerImgs: Array
  },
  data () {
    return {
      showGallery: false,
      imgs: [
        // 'http://img1.qunarzz.com/sight/p55/201211/04/fbcab3e5d6479ce893835fbb.jpg_r_800x800_6360f514.jpg',
        // 'http://img1.qunarzz.com/wugc/p180/201306/16/7f08e81624346b1693835fbb.jpg_r_800x800_5f03ad73.jpg'
      ]
    }
  },
  components: {
    CommonGallary
  },
  methods: {
    handleBannerClick () {
      this.showGallery = true
    },
    handlegalleryClose () {
      this.showGallery = false
    }
  }
}
</script>
<style lang="stylus" scoped>
.banner
  overflow:hidden
  height:0
  padding-bottom:55%
  position:relative
  .banner-img{
    width:100%
  }
  .banner-info
    display:flex
    position:absolute
    left:0
    right:0
    bottom:0
    line-height:0.6rem
    background-image:linear-gradient(top,rgba(0,0,0,0),rgba(0,0,0,0.8))
    color:#fff
    .banner-title
      flex:1
      font-size:.32rem
      padding:0 .2rem
    .banner-number
      padding:0 .4rem
      height:.32rem
      line-height:.4rem
      margin-top:.24rem
      border-radius:.2rem
      background:rgba(0,0,0,.8)
      font-size:0.24rem
      .iconfont
        font-size:.24rem

</style>

接下来我们给项目添加一些动画效果,点击我们的banner图片,进入gallery图片,给他一个动画的效果
封装动画组件,transition里面的slot插槽,可以插入各种我们要进行动画处理的组件

//src\common\fade\FadeAnimation.vue
<template>
    <div>
        <transition>
            <slot></slot>
        </transition>
    </div>
</template>
<script>
export default {
  name: 'FadeAnimation'
}
</script>
<style lang="stylus" scoped>
.v-enter,.v-leave-to
    opacity:0
.v-enter-active,.v-leave-active
    transition: opacity 0.5s
</style>

我们在gallery中使用
跟我一起做一个vue的小项目(去哪儿网APPvue2.5完结篇)
效果如下
跟我一起做一个vue的小项目(去哪儿网APPvue2.5完结篇)

后记:今天接到了一个电话,大抵是以前实习的时候的,问我同学,说是打我同学电话没有打到,然后问我有没有同他联系。有些唏嘘。
想想毕业已经三年了。
不知道自己对自己的未来有什么期许呢~你想成为什么样的人呢?是不是三年前自己想成为的人呢?
好好看视频,好好学习,好好掌握项目吧~我呀, 还差的很远呢~

第十部分主要讲解的是项目接口联调部分,将mock中的数据放入XAMPP中,还有如何手机真机测试,以及如何打包数据,这部分我就是看了而已,没有实际操作。真实数据是与后端联调。视频的老师给了有些学习vue的建议,可以多多研究vue的一些常见组件的源码,还有研究vue的源码,以及,学习vue的ssr服务端的渲染问题等。

还有很多东西不会,要精通,要很厉害啊~~~

跟我一起做一个vue的小项目(去哪儿网APPvue2.5完结篇)

上一篇:POJ-2486 Apple Tree 树形DP


下一篇:Android native进程间通信实例-socket本地通信篇之——服务端进程异常退出解决办法