Alphabet.vue 子传父
- 当点击某个字母时,这个事件方法会接收到一个e的事件对象
子传父
调用this.$emit向外触发事件,change是事件名,e.target.innerText是事件携带的内容
<template>
<ul class="list">
<li
class="item"
v-for="(item, key) in cities"
:key="key"
@click="handleLetterClick"
>
{{ key }}
</li>
</ul>
</template>
<script>
export default {
name: 'Alphabet',
props: {
cities: Object
},
methods: {
// 当点击某个字母时,这个事件方法会接收到一个e的事件对象
// 调用this.$emit向外触发事件,change是事件名,e.target.innerText是事件携带的内容
handleLetterClick(e) {
this.$emit('change', e.target.innerText)
// console.log(e.target.innerText)
}
}
}
</script>
<style lang="scss" scoped>
@import '~styles/varibles.scss';
.list {
display: flex;
flex-direction: column;
justify-content: center;
position: absolute;
top: 1.58rem;
right: 0;
bottom: 0;
width: 0.4rem;
.item {
text-align: center;
line-height: 0.4rem;
color: $bgColor;
}
}
</style>
City.vue 父传子
- 在list组件上监听change事件;接收letter值
- 父传子
:letter=“letter”
<template>
<div>
<city-header></city-header>
<city-search></city-search>
<city-list :hot="hotCities" :cities="cities" :letter="letter"></city-list>
<city-alphabet
:cities="cities"
@change="handleLetterChange"
></city-alphabet>
</div>
</template>
<script>
import CityHeader from './components/Header'
import CitySearch from './components/Search'
import CityList from './components/List'
import CityAlphabet from './components/Alphabet'
import axios from 'axios'
export default {
name: 'City',
data () {
return {
cities: {},
hotCities: [],
letter: ''
}
},
components: {
CityHeader,
CitySearch,
CityList,
CityAlphabet
},
methods: {
getCityInfo () {
axios({
url: '/static/mock/city.json',
method: 'get'
}).then(this.handleGetCityInfoSucc)
},
handleGetCityInfoSucc (res) {
console.log(res)
if (res.data.ret && res.data.data) {
this.cities = res.data.data.cities
this.hotCities = res.data.data.hotCities
}
},
handleLetterChange (letter) {
this.letter = letter
// console.log(letter)
}
},
mounted () {
this.getCityInfo()
}
}
</script>
<style lang="scss" scoped></style>
List.vue watch监听
- props里设置 letter: String
watch
来监听letter- 利用better-scroll提供给我们的接口,如果letter值不为空时,调用
this.scroll.scrollToElement()
方法;让better-scroll的滚动区域自动滚到某个元素上
<template>
<div class="list" ref="wrapper">
<div>
<div class="area">
<div class="title border-topbottom">您的位置</div>
<div class="button-list">
<div class="button-wrapper">
<div class="button">北京</div>
</div>
</div>
</div>
<div class="area">
<div class="title border-topbottom">热门城市</div>
<div class="button-list">
<div class="button-wrapper" v-for="item in hot" :key="item.id">
<div class="button">{{ item.name }}</div>
</div>
</div>
</div>
<div class="area" v-for="(item, key) in cities" :key="key" :ref="key">
<div class="title border-topbottom">{{ key }}</div>
<div class="item-list">
<div
v-for="innerItem in item"
:key="innerItem.id"
class="item border-bottom"
>
{{ innerItem.name }}
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import BScroll from 'better-scroll'
export default {
name: 'CityList',
props: {
hot: Array,
cities: Object,
letter: String
},
updated () {
this.scroll = new BScroll(this.$refs.wrapper)
},
// 监听letter
watch:{
letter (){
// console.log(this.letter)
// better-scroll提供给我们这样一个接口,如果letter不为空时,调用this.scroll.scrollToElement()方法;让better-scroll的滚动区域自动滚到某个元素上
if(this.letter){
const element = this.$refs[this.letter][0]
this.scroll.scrollToElement(element)
}
}
}
}
</script>
<style lang="scss" scoped>
@import '~styles/varibles.scss';
// 1像素边框问题
.border-topbottom {
&::before {
border-color: #ccc;
}
&::after {
border-color: #ccc;
}
}
.border-bottom {
&::before {
border-color: #ccc;
}
}
.list {
overflow: hidden;
position: absolute;
top: 1.58rem;
left: 0;
right: 0;
bottom: 0;
.title {
padding: 0.1rem 0.2rem;
line-height: 0.44rem;
background: #eee;
}
.button-list {
overflow: hidden;
padding: 0.1rem 0.6rem 0.1rem 0.1rem;
.button-wrapper {
float: left;
width: 33.33%;
.button {
margin: 0.1rem;
padding: 0.14rem 0;
text-align: center;
border: 0.02rem solid #ccc;
border-radius: 0.1rem;
}
}
}
.item-list {
.item {
line-height: 0.76rem;
background: #fff;
padding-left: 0.2rem;
}
}
}
</style>
字母表组件进行滚动事件监听
在页面上下滑动时,现在所在的位置是第几个字母?
思路是:
首先获得A字母距离顶部的高度,然后滑动是看下当前手指位置距离顶部的高度;做一个差值就能算出当前手指距离A字母顶部的高度,这个差值再除以每个字母的高度,就知道当前是哪个字母了,然后取对应的字母触发change事件给外部。
在这之前先定义标识位,只有start之后才可以move这个顺序
<template>
<ul class="list">
<li
class="item"
v-for="item in letters"
:key="item"
@click="handleLetterClick"
@touchstart="handleTouchstart"
@touchmove="handleTouchMove"
@touchEnd="handleTouchEnd"
:ref="item"
>
{{ item }}
</li>
</ul>
</template>
<script>
export default {
name: 'Alphabet',
props: {
cities: Object
},
// 定义标识位,只有start之后才可以move这个顺序
data () {
return {
touchStatus: false
}
},
computed: {
letters () {
const letters = []
for (let i in this.cities) {
letters.push(i)
}
return letters
}
},
methods: {
// 当点击某个字母时,这个事件方法会接收到一个e的事件对象
// 调用this.$emit向外触发事件,change是事件名,e.target.innerText是事件携带的内容
handleLetterClick (e) {
this.$emit('change', e.target.innerText)
// console.log(e.target.innerText)
},
handleTouchstart () {
this.touchStatus = true
},
handleTouchMove (e) {
if (this.touchStatus) {
// A元素距离顶部的高度
const startY = this.$refs['A'][0].offsetTop
// console.log(startY)
// 当前手指位置距离顶部的高度(包含头部高度79)
const touchY = e.touches[0].clientY - 79
// console.log(touchY)
// 求差值
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="scss" scoped>
@import '~styles/varibles.scss';
.list {
display: flex;
flex-direction: column;
justify-content: center;
position: absolute;
top: 1.58rem;
right: 0;
bottom: 0;
width: 0.4rem;
.item {
text-align: center;
line-height: 0.4rem;
color: $bgColor;
}
}
</style>
列表切换性能优化
当我们的手指在字母表上下滑动时,handleTouchMove ()方法就会被执行,这样性能是比较低的。需要优化一下。
主要优化在两个方面:
- startY拿出来,是因为这部分是固定的大小,代表A字母距离页面顶部的距离,是固定的;每次调用handleTouchMove 方法都会重新计算赋值,造成性能浪费
- 函数节流采用的方式是通过延迟来减少handleTouchMove 方法的执行频率;延时16毫秒,防止这部分代码计算过于频繁。每16毫秒执行一次,人眼也很难捕捉到这部分延迟,对页面来说是节省了无谓的消耗
<template>
<ul class="list">
<li
class="item"
v-for="item in letters"
:key="item"
@click="handleLetterClick"
@touchstart="handleTouchstart"
@touchmove="handleTouchMove"
@touchEnd="handleTouchEnd"
:ref="item"
>
{{ item }}
</li>
</ul>
</template>
<script>
export default {
name: 'Alphabet',
props: {
cities: Object
},
// 定义标识位,只有start之后才可以move这个顺序
data() {
return {
touchStatus: false,
startY: 0,
timer: ''
}
},
// 当页面数据被更新时,同时页面完成自己的渲染之后,这个钩子就会执行
//startY拿出来,是因为这部分是固定的大小,代表A字母距离页面顶部的距离,是固定的
//每次调用move方法都会重新计算赋值,造成性能浪费
updated() {
this.startY = this.$refs['A'][0].offsetTop
},
computed: {
letters() {
const letters = []
for (let i in this.cities) {
letters.push(i)
}
return letters
}
},
methods: {
// 当点击某个字母时,这个事件方法会接收到一个e的事件对象
// 调用this.$emit向外触发事件,change是事件名,e.target.innerText是事件携带的内容
handleLetterClick(e) {
this.$emit('change', e.target.innerText)
// console.log(e.target.innerText)
},
handleTouchstart() {
this.touchStatus = true
},
handleTouchMove(e) {
console.log(222)
if (this.touchStatus) {
if (this.timer) {
clearTimeout(this.timer) // 关闭 setTimeout() 返回的 ID 值。该值标识要取消的延迟执行代码块
}
//timer变量保存settimeout返回的id,标识这个延迟代码块
this.timer = setTimeout(() => {
console.log(3333)
// 当前手指位置距离顶部的高度(包含头部高度79)
const touchY = e.touches[0].clientY - 79
// console.log(touchY)
// 求差值
const index = Math.floor((touchY - this.startY) / 20)
if (index >= 0 && index < this.letters.length) {
this.$emit('change', this.letters[index])
}
}, 16) //延时16毫秒,防止这部分代码计算过于频繁。每16毫秒执行一次,人眼也很难捕捉到这部分延迟,对页面来说是节省了无谓的消耗
}
},
handleTouchEnd() {
this.touchStatus = false
}
}
}
</script>
<style lang="scss" scoped>
@import '~styles/varibles.scss';
.list {
display: flex;
flex-direction: column;
justify-content: center;
position: absolute;
top: 1.58rem;
right: 0;
bottom: 0;
width: 0.4rem;
.item {
text-align: center;
line-height: 0.4rem;
color: $bgColor;
}
}
</style>
这个滚动监听对于我现在水平太难了!!!!!!为了缓解一下情绪,我决定了!!!点一份我爱的拆骨肉疙瘩汤缓解一下!!
先存一下笔记吧,等以后水平够了回来继续看,加油!奥利给!!!!!!