目标: 实现兄弟组件之间的联动

(一) 点击城市页面的Alphabet.vue中的字母能够跳转到对应的List.vue的位置

  1. 给Alphabet.vue中的每一个循环的li绑定一个点击事件<li class="item" v-for="(item,key) of cities" :key="key" @click="handleLetterClick">{{key}}</li>,并将这个事件定义到methods对象中,当点击某一个字母的时候这个事件会接收到一个事件对象e,console.log(e.target.innerText) 会发现,在页面上点击字母的时候在终端就会显示相应的字母。 希望的是: 我们把这个字母传递给List.vue组件, 让其对应的区块显示出来。

  2. 兄弟组件的传值: 兄弟组件是非父子组件,可以使用bus总线的方式,而这里的非父子组件就是一个兄弟组件,我们可以将Alphabet.vue中点击字母获取到的数据传递给City.vue组件,然后再由这个组件转发给List.vue组件

Alphabet.vue组件中触发点击事件获取到数据,使用this.$emit('change', e.target.innerText)的方式向外触发事件,事件的名字是change,携带的数据是e.target.innerText

然后, 由City.vue组件来监听这个事件,即<city-alphabet :cities="cities" @chang="handleLetterChange"></city-alphabet>。在methods中定义这个事件,并且它会接收到一个letter数据,这个数据就是由子组件Alphabet.vue传递过来的那个点击的字母。

现在,将letter转发给List.vue。这里就是父组件向子组件传递数据了,是通过属性的形式传递的, 首先在data函数中定义一个数据letter,默认为一个空字符串,当接收到外部传来的letter数据的时候,即:

handleLetterChange (letter) {this.letter = letter
}

最后就只需要将这个letter传递给city-list就可以了:<city-list :cities="cities" :hotCities="hotCities" :letter="letter"></city-list>

父组件传递了一个letter,子组件List.vue就要接收这个letter:

props: {cities: Object,hotCities: Array,letter: String
}

这个时候,子组件就监听到由父组件传递过来的这个letter的内容了。我们想要做的是: 当我监听到这个letter发生变化的时候,我要把本组件中与letter值相同的那个城市列表项显示出来。

实现:借助vue自带的侦听器watch,在watch中侦听letter的变化。better-scroll提供了一个接口scrollToElement(),它可以让better-scroll的滚动区自动的滚到某一个元素上。具体的,先给循环区域添加一个ref::ref="key" ,就可以通过this.$refs获取到这个字母this.letter对应的class="area"的这个区域:

watch: {letter () {if (this.letter) {const element = this.$refs[this.letter]this.scroll.scrollToElement(element)}}
}

上述代码会报错,原因是ref是通过循环输出的,this.$refs[this.letter]或得到的内容是一个数组而不是一个标准的DOM元素,this.scroll.scrollToElement(element)的参数element需要是一个DOM元素或者是一个DOM的选择器,所以需要修改为:

watch: {letter () {if (this.letter) {const element = this.$refs[this.letter][0]this.scroll.scrollToElement(element)}}
}

(二) 希望在右侧的字母表上做上下拖拽的时候,也会导致左侧的列表项的相应变动

首先,给Alphabet组件中的li绑定三个touch事件,即@touchstart="handleTouchStart" @touchmove="handleTouchMove" @touchend="handleTouchEnd",并定义相应的事件函数。然后,在data中设置一个标识位:touchStatus: false,当手指触摸的时候,将这个值设置为true,当手指停止触摸的时候设置为false。只有当touch状态为true的时候才去做move的处理,此时代码是这样的:

handleTouchStart () {this.touchStatus = true
},
handleTouchMove () {if (this.touchStatus) {}
},
handleTouchEnd () {this.touchStatus = false
}

其次,在右侧的字母表处上下滑动的时候,想要直接获取当前手指所在的是哪一个字母是比较困难的,我们的思路是: 首先获得A这个字母距离顶部的高度,然后获得当前滑动的时候距离顶部的高度,做一个差值,就能够获取当前字母与A字母之间的高度,再除以每个字母的高度,就可以知道当前是哪一个字母了。这样的话,去取对应的字母,触发一个change事件给外部就可以了。

如果想要根据下标找到这个下标对应的字母的话,就需要有个数组来存储这个字母的列表, 我们之前使用的cities是一个对象而不是一个数组,所以需要定义一个数组类型的数据,在这里设置一个计算属性:

computed: {letters () {const letters = []for (let i in this.cities) {letters.push(i)}return letters}
}

其返回的结果是一个数组,形式是[‘A’,‘B’,‘C’,…]。有了这个数组,之前的循环就可以做以下修改:

<ul><li class="item" v-for="item of letters" :key="item"@click="handleLetterClick"@touchstart="handleTouchStart"@touchmove="handleTouchMove"@touchend="handleTouchEnd">{{item}}</li>
</ul>

当我们上下拖动的时候,即this.touchStatus = true的时候,需要计算一下A字母距离顶部的高度,方法是:设置一个ref属性来获取A元素距离顶部的高度:

<ul><li class="item" v-for="item of letters" :key="item":ref="item"@click="handleLetterClick"@touchstart="handleTouchStart"@touchmove="handleTouchMove"@touchend="handleTouchEnd">{{item}}</li>
</ul>

接下来,就可以按照之前的思路进行位置的计算:

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

其中,const startY = this.$refs['A'][0].offsetTop计算的是字母A距离头部蓝色区域的下边沿的高度。handleTouchMove的时候会接收到一个事件参数对象e,在这个事件对象中会有一个touches的数组,第0项表示的就是我手指触摸屏幕的一些信息,于是可以获取到手指的clientY的值,而这个值表示的是当前的位置到屏幕最顶部的距离,要想计算当前位置到蓝色区域下边沿的高度,还要减去头部蓝色区域的高度,即e.touches[0].clientY - 79。index就是当前字母的下标了,公式中的20表示的是每个字母的高度。当索引值在0到this.letters.length之间的时候就可以向外触发change事件了,传递的数据就是this.letters[index]

(三) 列表切换性能优化

  1. handleTouchMove方法中,当我们的手指在右侧的字母表上进行上下滑动的时候,这个方法就会被执行,其中A字母的offsetTop一直都是固定的,而每次执行这个方法的时候,这里都会计算一次,所以性能就会比较低。解决方法: 在data中定义一个变量startY,初始值设置为0,然后再写一个updated的生命周期钩子,当页面的数据被更新的时候,并且页面完成了自己的渲染之后,updated这个钩子就会执行,即:
updated () {this.startY = this.$refs['A'][0].offsetTop
}

分析: 当初次渲染Alphabet.vue的时候, cities的数值化值是一个空对象,页面刚加载的时候,Alphabet.vue里面什么东西都不会显示出来,当City.vue通过ajax获取到数据之后,cities的值才发生变化,Alphabet.vue才会被渲染出来,当向Alphabet.vue中传递的数据发生变化的时候,它就会重新渲染,当Alphabet.vue被重新渲染之后,这个updated生命周期钩子就会被执行,这时,页面上已经展示出了字母列表的所有内容,这时去获取字母A的offsetTop就不会有问题,拖动效果依然能够实现。

  1. 函数节流:

当我们的鼠标在字母表上来回的移动的时候,touchmove执行的频率是非常高的,我们可以通过节流来限制一下函数执行的频率。

在数据项中定义一个变量timer,默认值为null,当在执行handleTouchMove的时候,首先在判断是否已经存在了,如果存在就清除掉这个timer,否则就创建一个timer:

handleTouchMove (e) {if (this.touchStatus) {if (this.timer) {clearInterval(this.timer)}this.timer = setTimeout(() => {const touchY = e.touches[0].clientY - 79const index = Math.floor((touchY - this.startY) / 20)if (index >= 0 && index < this.letters.length) {this.$emit('change', this.letters[index])}}, 16);}
}

优化完成。

代码提交

去哪儿-11-city-components相关推荐

  1. Vue 实例实战之 Vue webpack 仿去哪儿网App页面开发(应用中的几个页面简单实现)

    Vue 实例实战之 Vue webpack 仿去哪儿网App页面开发(应用中的几个页面简单实现) 目录 Vue 实例实战之 Vue webpack 仿去哪儿网App页面开发(应用中的几个页面简单实现) ...

  2. 去哪儿-21-debuggiing-testing

    目标: Vue项目的接口联调,真机测试与webpack打包上线 1. Vue项目的接口联调 之前我们在写代码的时候,使用的ajax数据都不是从后端返回的真实数据,而是我们自己通过接口mock模拟的假数 ...

  3. 去哪儿-10-city-ajax

    目标: 城市页面的动态数据渲染 1. 准备阶段: 创建分支,构建组件模板,引入组件. 与首页一样,将我们的city.json数据放在静态资源文件夹内,并让City.vue组件来获取ajax数据. 2. ...

  4. How to recover from root.sh on 11.2 Grid Infrastructure Failed

    从10g的clusterware到11g Release2的Grid Infrastructure,Oracle往RAC这个框架里塞进了太多东西.虽然照着Step by Step Installati ...

  5. listrecord根据某个属性去重_去哪网开发实战记录(9):城市选择页(中)

    兄弟组件之间的联动 所谓的兄弟组件之间的联动,其实就是实现点击右侧的字母就能跳转至对应的首字母城市,因此列表组件需要知道右侧的字母列表的点击事件所对应的元素字母,这就需要兄弟组件间的数据传递了(Alp ...

  6. 《孙子兵法》十三篇注译(11--地形篇)

    王可伟 作品  地形篇(第十) (读<孙子兵法>,悟管理.营销之道) 本篇论述军事地形学上的问题.孙子通过"地有六形"."兵有六败"的论述,揭示了敌 ...

  7. 电磁兼容工程(Electromagnetic compatibility engineering Herry Ott )读书笔记-- 章11 数字电路电源分布

    目录 11  数字电路电源分布 11.1 电源去耦 11.2 电源管脚的瞬态电流 11.2.1 瞬态负载电流 11.2.2  动态内部电流 11.2.3 瞬态电流的傅里叶频谱 11.2.4 总共的瞬态 ...

  8. 全网最详细的重装Windows 11遇到的网络问题

    序言 哈哈哈哈好久不见了,最近忙着学业就没怎么发Linux的文章 今天就出一篇关于Windows 11怎么重装系统,这里以联想电脑为例 目录 一.前言 二.准备一个空白U盘,官网下载Windows 1 ...

  9. 有梦想就去追,程序员辞职组乐队被老板资助

    程序员被老板资助玩乐队是什么感受? 29岁的赵东晓是某公司客户端的开发工程师,大学期间,他组建了沉迷乐队,不仅拿下了2013年迷笛全国校园乐队大赛总冠军,2015年更受到央视的关注,为其拍摄专题纪录片 ...

  10. python爬取景点数据看该去哪里玩——伊犁篇

    写在开头 六月的新疆美如天境,一直想去自驾游,在网易云课堂看到城市数据团大鹏老师讲的<用数据做攻略:找到一个城市最有趣的地方>传送门,于是尝试用python爬取景点数据,进行综合评价,然后 ...

最新文章

  1. TransactionTemplate和@Transactional注解的区别
  2. WQL Analyzer V1.1(WMI查询分析器)
  3. html中如何让三个方块并排,html – 并排设置两个div,然后设置第三个div
  4. python找不到模块文件夹_python – __init__.py在同一目录中找不到模块
  5. [模拟|数位] leetcode 9 回文数
  6. 拒绝空谈 AI 设想!手把手教你构建实时、高可用的 AI 调度平台
  7. freetype字体位图转距离场_关于freetype的移植和其官方demos的使用总结
  8. ResNet网络结构搭建
  9. java 什么是过滤器_java中的过滤器是什么
  10. 局域网服务器时间修改,局域网内建立时间同步
  11. 计算机网络4小时速成:应用层,cs模型,p2p模型,DNS域名系统,文件传输协议FTP,电子邮件SMTP,万维网HTTP,动态主机配置协议DHCP
  12. python开方 运算符_[转载] Python中的算数运算符
  13. C#中的Obsolete特性
  14. EagleEye论文+代码
  15. halcon面阵相机标定矫正
  16. SSM框架项目实践,leetcode46
  17. 输出日期为2021年的第几天(switch...case)
  18. 四川乐而得教育:拼多多店铺推广要注意哪些优化
  19. 2020《更好的明年》广东卫视跨年演讲
  20. 说说我理解的SVN操作

热门文章

  1. Spring @Autowired Annotation
  2. DLL/OCX文件的注册与数据执行保护DEP
  3. 行为模式之备忘录模式
  4. JRebel Idea热更新插件
  5. php 命令行打印换行符_如何在命令行输出中打印换行符
  6. 技术文档的撰写_如何撰写出色的技术博客文章
  7. ruby on rails_如何在Ruby on Rails应用中用Vue.js替换jQuery
  8. 简易自动电阻测试仪_开始自动测试您的网站的简单方法
  9. java this的用法
  10. vscode用作markdown入门2--代码上下标目录字体