【vue】三、vue2仿去哪儿app——城市列表页面

三、vue2仿去哪儿app——城市列表页面

Ⅰ 页面结构

城市列表页面

  • 搜索框
  • 热门城市
  • 城市列表
  • 右侧字母表导航

在这里插入图片描述

Ⅱ 开发笔记及注意点

1.使用Better-scroll第三方包实现拖动
npm install better-scroll --save

引入better-scroll:import Bscroll from 'better-scroll'

使用vue中的ref属性来获取DOM元素。
在List.vue中,总组件需要有一个div包裹住其他的组件,并且在这个div上加上ref属性:

<div class="list" ref="wrapper">

使用mounted()函数,在页面DOM挂载完毕的时候,创建better-scroll实例属性,并将wrapper传进去:

mounted () {
    this.scroll = new Bscroll(this.$refs.wrapper, {click: true})
  }

注意:在使用better-scroll时,会使click事件失效
原因:better-scroll 默认会阻止浏览器的原生 click 事件。
因此,需要我们在创建实例属性时,将click属性设置为true。

2.1. 使用router-link实现页面的跳转
<router-link to='/city'>
//点击后跳转的控件
</router-link>

注意:加了这个组件之后,需要重新设置字体颜色,因为组件中包含一个链接,链接本身设置了字体颜色。

2.2 router点击后变色问题

因为router-link也有超链接样式,点击之后会变色,所以要给这一层的盒子加一个固定的颜色color。否则点击过后颜色就改变了

3.实现点击字母表某个字母,跳转到相应字母的城市列表项(兄弟组件间联动)

首先,要实现兄弟组件间的传值,则可以采取以下方法:
(1)首先,将Alphabet.vue的letter值传递给父组件City.vue;
(2)接着,父组件接收子组件Alphabet.vue传递过来的值,并将该值传递给子组件List.vue

在Alphabet.vue中,绑定click事件函数,在事件函数中,使用$e在这里插入代码片mit()函数触发父组件自定义事件,并将letter值传过去:
(1)div上:@click="handleLetterClick"
(2)事件函数:

handleLetterClick (e) {
      this.$emit('change', e.target.innerText)
    },

父组件City.vue中,在data中定义letter属性,并设值为空,在change事件所绑定的函数中,接收letter值,并设置为自定义的letter值,最后将自定义的letter值传递给子组件List.vue:
(1)change事件绑定函数:

<city-alphabet :cities='cities' @change="handleLetterChange"></city-alphabet>

(2)data中定义letter值:

data () {
    return {
      letter: ''
    }
  },

(3)事件函数:

handleLetterChange (letter) {
      this.letter = letter
    }

(4)将自定义的letter值传递给子组件List.vue:

<city-list :hot='hotCities' :cities='cities' :letter="letter"></city-list>

子组件List.vue中,接收父组件传来的letter值,借助监听器,当letter值改变时,调用better-scroll提供的接口:scrollToElement():让better-scroll自动滚到某个元素上。
(1)在props接收父组件的值:

props: {
    letter: String
  },

(2)借助监听器,监听letter值的改变:只要letter发生变化,就会执行该函数。
首先,在列表区域通过ref属性获取相应字母列表DOM元素:

<div class="area" v-for="(item,key) of cities" :key="key" :ref="key">

接着,在watch监听器中,获取当前字母的城市列表,

调用better-scroll提供的接口:scrollToElement():让better-scroll自动滚到字母对应的城市列表上:

watch: {
    letter () {
      if (this.letter) {
        const element = this.$refs[this.letter][0]
        this.scroll.scrollToElement(element)
      }
    }
  },
4.在左侧字母表中做上下拖拽的时候,城市列表内容也跟着变化

主要是监听拖拽时间,在li增加了三个事件,分别是touchstart、touchmove、touchend来监听拖拽事件,同时在li上使用ref,在data定义了一个touchStatus来保存是否在拖拽的状态,并使用计算属性把cities中的字母取出来放在数组letters中,最后计算拖拽到哪个字母:

    handleTouchStart () {
      this.touchStatus = true
    },
    handleTouchMove (e) {
      if(this.touchStatus){
        const startY = this.$refs['A'][0].offsetTop
        const touchY = e.touches[0].clientY -79
        const index = Math.floor((touchY-startY) / 20)
        if(index >= 0 && index < this.letters.length) {
          this.$emit('change',this.letters[index])
        }
      }
    },
    handleTouchEnd () {
      this.touchStatus = false
    }

Alphabet.vue:

<template>
  <ul class="list">
    <li 
      class="item" 
      v-for="item of letters" 
      :key="item"
      :ref="item"
      @touchstart="handleTouchStart"
      @touchmove="handleTouchMove"
      @touchend="handleTouchEnd"
      @click="handleLetterClick"
    >
      {{item}}
    </li>
  </ul>
</template>
<script>
export default {
  name:'CityAlphabet',
  props: {
   cities: Object
  },
  data () {
    return {
      touchStatus: false
    }
  },
  computed: {
    letters () {
      const letters = []
      for( let i in this.cities) {
        letters.push(i)
      }
      return letters
    }
  },
  methods: {
    handleLetterClick (e) {
      this.$emit('change',e.target.innerText)
    },
    handleTouchStart () {
      this.touchStatus = true
    },
    handleTouchMove (e) {
      if(this.touchStatus){
        const startY = this.$refs['A'][0].offsetTop
        const touchY = e.touches[0].clientY -79
        const index = Math.floor((touchY-startY) / 20)
        if(index >= 0 && index < this.letters.length) {
          this.$emit('change',this.letters[index])
        }
      }
    },
    handleTouchEnd () {
      this.touchStatus = false
    }
  }
}
</script>
<style lang="stylus" scoped>
  @import '~styles/varibles.styl'
  .list
    display flex
    flex-direction column
    justify-content center
    position absolute
    top 1.58rem
    right 0
    bottom 0
    width .4rem
    .item
      line-height .4rem
      text-align center
      color $bgColor
</style>
5.Vuex的高级使用及localStorage
import Vue from 'vue'
import Vuex from 'vuex'
 
Vue.use(Vuex)
 
export default new Vuex.Store({
  state: {
    city: localStorage.city || '成都'
  },
  mutations: {
    changeCity (state, city) {
      state.city = city
      localStorage.city = city
    }
  }
})

上面实现的功能会有问题,就是每次重启应用的时候,选择的城市会重置为State中预设的城市名,为了解决这个问题,在这里就引入了h5中的localStorage本地缓存来实现

import Vue from 'vue'
import Vuex from 'vuex'
 
Vue.use(Vuex)
 
let defaultCity = '上海'
try {
  if (localStorage.city) {
    defaultCity = localStorage.city
  }
} catch (e) {}
 
export default new Vuex.Store({
  state: {
    city: defaultCity
  },
  mutations: {
    changeCity (state, city) {
      state.city = city
      try {
        localStorage.city = city
      } catch (e) {}
    }
  }
})
6.使用函数节流,限制move方法的频率
  1. 首先,在data中定义timer=null
  2. 在handleTouchMove ()方法中,当timer不为空时,清除timer;若为空,则定义定时器:
handleTouchMove (e) {
      if (this.touchStatus) {
        if (this.timer) {
          clearTimeout(this.timer)
        }
        this.timer = setTimeout(() => {
          const touchY = e.touches[0].clientY - 79
          const index = Math.floor((touchY - this.startY) / 20)
          if (index >= 0 && index < this.letters.length) {
            this.$emit('change', this.letters[index])
          }
        }, 10)
      }
    },

7.使用keep-alive优化网页性能

当前页面存在一个问题:每次路由切换,总会发送一次ajax请求(每次切换,页面总会重新渲染,mounted钩子重新执行,ajax数据就会重新获取)。
为了优化网页的性能,使用keep-alive:实现路由加载一次过后,会将加载内容放入内存之中。下一次进去,不需要重新渲染这个组件,mounted()函数也不再执行(即不再发送ajax请求),只需要从内存将内容取出显示

<keep-alive exclude="Detail">
    <router-view/>
</keep-alive>

在使用keep-alive之后,重新刷新页面时,mounted()函数都不会再次执行,导致如果改变城市后,首页其他数据没有相应的重新获取以及改变。

还有逻辑问题,就是在重新选择城市后在Home.vue中需要重新进行Ajax请求,

在Home.vue中引入vuex

import { mapState } from 'vuex'

修改getHomeinfo方法,在请求的时候带上state中的city

getHomeinfo () {
    axios.get('/api/index.json?city=' + this.city)
      .then(this.getHomeinfoSucc)
},
activated () {
    if (this.lastCity !== this.city) {
      this.lastCity = this.city
      this.getHomeInfo()
    }
  }

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序媛小y

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值