验证中...
7.21 杭州源创会火热报名中,一起来看看移动开发如何紧跟浪潮?
片段 1 片段 2 片段 3 片段 4 片段 5 片段 6
1、父组件 City
原始数据 复制代码
<template>
<CityHeader></CityHeader>
<CitySearch :cities="cities"></CitySearch>
<CityList :cities="cities" :letter="letter"></CityList>
<!-- 把 data 里的 letter 绑定到 letter 变量,并传递给 List 子组件-->
<CityAlphabet :cities="cities" @change="handleLetterChange"></CityAlphabet>
<!-- 父组件监听 change 事件,并执行 handleLetterChange 方法-->
</template>
<script>
import axios from 'axios'
import CityList from './components/List'
import CityAlphabet from './components/Alphabet'
export default {
name: 'City',
components: { CityList, CityAlphabet },
data: function () {
return {
hotCities: [],
cities: {},
letter: ''
}
},
mounted () {
this.getCityInfo()
},
methods: {
getCityInfo: function () {
axios.get('/api/city.json')
.then(this.handleGetCityInfoSucc)
},
handleGetCityInfoSucc: function (res) {
res = res.data
if(res.ret && res.data) {
const data = res.data
this.cities = data.cites
}
},
handleLetterChange: function (letter) {
this.letter = letter
// 接收从子组件传递过来的参数 letter ,并赋给 data 里的 letter
}
}
}
</script>
2、子组件 Alphabet
原始数据 复制代码
<template>
<ul class="list">
<li class="item"
v-for="item of letters"
:key="item"
@click="handleLettersClick"
>
{{item}}
</li>
</ul>
</template>
<script>
export default {
name: 'CityAlphabet',
props: {
cities: Object
},
computed: {
letters: function () {
const letters = []
for(let i in this.cities){
letters.push(i)
}
return letters
}
},
methods: {
handleLettersClick: function (e) {
this.$emit('change', e.target.innerText)
// 子组件触发 change 事件,并把 e.target.innerText 作为参数传递给父组件
}
}
}
</script>
3、子组件 List
原始数据 复制代码
<template>
<div class="list" ref="wrapper">
<div>
<div class="area"
v-for="(alphabet, key) in cities"
:key="key"
:ref="key"
>
<!-- 因为:ref 绑定在循环上,所以 this.$refs.XXX 或者 this.$refs[XXX] 获取到的是一个数组 -->
<div class="title border-topbottom">{{key}}</div>
<div class="item-list">
<div class="item border-bottom" v-for="item in alphabet" :key="item.id">{{item.name}}</div>
</div>
</div>
</div>
</div>
</template>
<script>
import Bscroll from 'better-scroll'
export default {
name: 'CityList',
props: {
cities: Object,
letter: String <!-- 接受从父组件传递过来的 letter -->
},
watch: {
letter: function () { <!-- 监听 letter 的变化,并实现兄弟组件间的数据传递和联动效果 -->
if (this.letter) {
const element = this.$refs[this.letter][0]
<!-- this.$refs[this.letter] 获取到的是一个数组,
加上 [0] 获取到的是 DOM 元素 -->
this.scroll.scrollToElement(element)
}
}
},
mounted () {
this.scroll = new Bscroll(this.$refs.wrapper)
}
}
</script>
4、接下来,我们在子组件 Alphabet 中实现滚动手指,子组件 List 滚动到相关位置
原始数据 复制代码
<template>
<ul class="list">
<li class="item"
v-for="item of letters"
:key="item"
:ref="item"
@click="handleLettersClick"
@touchstart="handleTouchStart"
@touchmove="handleTouchMove"
@touchEnd="handleTouchEnd"
>
<!-- 绑定三个事件:touchstart、touchmove、touchend ,并给每一项,加上 :ref -->
{{item}}
</li>
</ul>
</template>
<script>
export default {
name: 'CityAlphabet',
props: {
cities: Object
},
data: function () {
return {
touchStatus: false // 定义一个标志 flag
}
},
computed: {
letters: function () {
const letters = []
for(let i in this.cities){
letters.push(i)
}
return letters
}
},
methods: {
handleLettersClick: function (e) {
this.$emit('change', e.target.innerText)
// 子组件触发 change 事件,并把 e.target.innerText 作为参数传递给父组件
},
handleTouchStart: function () {
this.touchStatus = true
},
handleTouchMove: function (e) {
if (this.touchStatus) {
// 原理是:获取当前移动手指所在字母,触发 change 事件,并把当前手指所在字母作为参数传递给父组件
// this.$refs['A'] 获取到的是一个数组,加上 [0] 获取到的才是 DOM 元素
// HTMLElement.offsetTop 为只读属性,它返回当前元素相对于其 offsetParent 元素的顶部的距离。
// clientY 事件属性返回当事件被触发时鼠标指针向对于浏览器页面(客户区)的垂直坐标。 客户区指的是当前窗口。
const startY = this.$refs['A'][0].offsetTop // 这里 startY 是字母 A 相对于父元素 ul 的垂直距离
const touchY = e.touches[0].clientY - 84 // touchY 是手指的当前字母相对于浏览器窗口距离 再减去顶部内容( CityHeader 组件和 CitySearch 组件)的距离
const index = Math.floor((touchY - startY) / 20) // index 为当前字母对应的下标,其中 20 是每个字母的高度
if(index >= 0 && index < this.letters.length) {
this.$emit('change', this.letters[index])
}
}
},
handleTouchEnd: function () {
this.touchStatus = false
}
}
}
</script>
5、性能优化:updated 生命钩子
原始数据 复制代码
<template>
<ul class="list">
<li class="item"
v-for="item of letters"
:key="item"
:ref="item"
@click="handleLettersClick"
@touchstart="handleTouchStart"
@touchmove="handleTouchMove"
@touchEnd="handleTouchEnd"
>
{{item}}
</li>
</ul>
</template>
<script>
export default {
name: 'CityAlphabet',
props: {
cities: Object
},
data: function () {
return {
touchStatus: false,
startY: 0 // 在 data 里定义 startY 的初始值
}
},
computed: {
letters: function () {
const letters = []
for(let i in this.cities){
letters.push(i)
}
return letters
}
},
updated () {
// updated 讲解:
// 当页面刚加载的时候,cities 是一个空对象,所以 Alphabet 子组件里面什么东西都不会显示出来,
// 当 ajax 获取到数据之后, cities 的值才发生变化,Alphabet 才被渲染出来。
// 当你往 Alphabet 传的数据发生变化的时候,Alphabet 就会重新渲染,当 Alphabet 重新渲染之后, updated() 生命周期钩子就会被执行
// 这个时候,页面上已经展示出了城市字母列表里所有的内容,这个时候,我们去获取字母 'A' 所在的 DOM 的 offsetTop 值,就没有任何问题
this.startY = this.$refs['A'][0].offsetTop
},
methods: {
handleLettersClick: function (e) {
this.$emit('change', e.target.innerText)
},
handleTouchStart: function () {
this.touchStatus = true
},
handleTouchMove: function (e) {
if (this.touchStatus) {
const touchY = e.touches[0].clientY - 84
const index = Math.floor((touchY - this.startY) / 20)
if(index >= 0 && index < this.letters.length) {
this.$emit('change', this.letters[index])
}
}
},
handleTouchEnd: function () {
this.touchStatus = false
}
}
}
</script>
6、性能优化:函数截流
原始数据 复制代码
<template>
<ul class="list">
<li class="item"
v-for="item of letters"
:key="item"
:ref="item"
@click="handleLettersClick"
@touchstart="handleTouchStart"
@touchmove="handleTouchMove"
@touchEnd="handleTouchEnd"
>
{{item}}
</li>
</ul>
</template>
<script>
export default {
name: 'CityAlphabet',
props: {
cities: Object
},
data: function () {
return {
touchStatus: false,
startY: 0,
timer: null // 定义 timer
}
},
computed: {
letters: function () {
const letters = []
for(let i in this.cities){
letters.push(i)
}
return letters
}
},
updated () {
this.startY = this.$refs['A'][0].offsetTop
},
methods: {
handleLettersClick: function (e) {
this.$emit('change', e.target.innerText)
},
handleTouchStart: function () {
this.touchStatus = true
},
handleTouchMove: function (e) {
if (this.touchStatus) {
if(this.timer) {
// 如果 this.timer 已经存在,我就把 this.timer 去除
clearTimeout(this.timer)
}
this.timer = setTimeout(()=>{ // 否则,创建一个 timer
const touchY = e.touches[0].clientY - 84
const index = Math.floor((touchY - this.startY) / 20)
if(index >= 0 && index < this.letters.length) {
this.$emit('change', this.letters[index])
}
},16)
// 讲解: 如果你已经正在做手指滚动这个事情,我让它延迟 16ms 之后再去执行,
// 假设在这个 16ms 之间,你又去做了手指的滚动,那么它会把上一次的操作清除掉,重新执行你这一次要做的事情
// 通过这种函数节流的方式,我们可以大大的节约 handleTouchMove 函数的执行频率,从而提高网页性能
}
},
handleTouchEnd: function () {
this.touchStatus = false
}
}
}
</script>

评论列表( 0 )

你可以在登录后,发表评论

11_float_left_people 11_float_left_close