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

兄弟组件之间的联动

所谓的兄弟组件之间的联动,其实就是实现点击右侧的字母就能跳转至对应的首字母城市,因此列表组件需要知道右侧的字母列表的点击事件所对应的元素字母,这就需要兄弟组件间的数据传递了(Alphabet组件与List组件之间的通信),可以使用到中央数据总线Bus,但是由于这里的业务逻辑不是很复杂,因此可以将Alphabet组件内的信息传递到City组件(子组件向父组件传递信息,发布订阅模式),然后City组件向List组件传递信息(父组件向子组件传递信息,属性传值方式)。

在gitee的分支栏点击新建分支city-components,然后记得将本地master分支切换到city-components分支。

字母点击跳转

打开Alphabet.vue文件,给字母表中的字母添加一个click事件,然后尝试将点击的字母在控制台上进行输出显示:

{{key}}

然后在script标签添加这个handleLetterClick方法:

methods: {    handleLetterClick (e) {      console.log(e.target.innerText)    }  }

可以测试一下当你点击右侧列表中的字母,控制台是否真的输出了对应的字母:

0d1670f5b87d8b62f4bc9156103094d8.png

前面说了由于这里的业务逻辑不是很复杂,因此可以将Alphabet组件内的信息传递到City组件(子组件向父组件传递信息,发布订阅模式),然后City组件向List组件传递信息(父组件向子组件传递信息,属性传值方式)。

那就开始编写使用发布订阅模式实现Alphabet组件内的信息传递到City组件的代码。修改子组件Alphabet.vue中handleLetterClick函数代码为:

methods: {    handleLetterClick (e) {    /** 注意此处必须使用innerText而不是innerHTML **/      this.$emit('change', e.target.innerText)    }  }

接着去父组件City.vue中监听该chang事件,请定义相应的handleLetterChange去接收子组件传递过来的信息:

handleLetterChange (letter) {      console.log(letter)    }

控制台测试发现当你点击右侧列表中的字母,控制台仍然输出了对应的字母。接下来就是父组件City.vue通过属性传值的方式将数据传递给子组件List.vue。修改父组件City.vue中handleLetterChange函数代码为:

   handleLetterChange (letter) {      this.letter = letter    }

然后在父组件中的data中返回letter,并将其通过属性传值给city-list组件:

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

然后在子组件List.vue中通过props来接收数据:

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

当List.vue发现letter有变化的时候,就显示跟letter首字母相同的城市列表,这种功能可以通过侦听器来实现:

watch: {    letter () {      console.log(this.letter)    }  }

控制台测试发现当你点击右侧列表中的字母,控制台仍然输出了对应的字母。

还记得之前推荐的那篇关于better-scroll的文章:当 better-scroll 遇见 Vue么,里面介绍了如何滚动至某个元素。既然是滚动至某个元素,那么肯定需要选择某个DOM节点了,Vue提供了ref来选择节点(List.vue):

然后使用侦听器来监听letter的变化:

  watch: {    letter () {      if (this.letter) {        console.log(this.$refs[this.letter])      }    }  }

当你点击某个字母时,控制台输出:

3d77c35803aa90b62ca6d49cbe564a86.png

而这个索引为0的div.area区域中包含了我们所需要的的城市列表信息:

2919a8e980d32b8a4ff6ccb5c2921334.png

既然这样就可以直接获取到对应的DOM节点,并将该节点传递给better-scroll,里面有一个scrollToElement方法,之后就能实现点击某个字母,城市列表页就会显示对应的城市信息:

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

拇指滑动跳转

前面介绍的是点击右侧的字母就能跳转至对应的首字母城市,其实比这个更普通的 就是当你拇指按住字母表上下滑动时,左边List组件也会相应的上下跳动。

实现这个需要,首先我们要监听使用者的手指,记录时候开始点击字母列表(touchstart),什么时候开始滚动(touchmove),以及什么时候离开字母列表(touchend)等等,自然而然地想到使用事件监听。注意我们还需要定义一个标志状态(touchStatus),默认为flase,只有当你手指触摸的时候才会变成true,其实就是指定这三个函数的执行顺序:

{{key}}

现在我们需要知道当你拇指在滑动的时候,你的拇指停留在哪个字母上,这件事情其实是较为复杂的,可以提供一种思路仅供参考:先获取A字母距离顶部的高度,然后当你滑动的时候获取你当前字母距离顶部的高度,接着将后者减去前者得到一个差值,最后用这个差值除于每个字母的长度就能得到这是第几个字母。然后让该字母触发对应的事件即可,那么这样你需要新建一个数组用于存放字母,然后根据索引来获取字母,可以使用计算属性:

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

上面的计算属性其实就是得到一个类似于['A','B','C','D']的数组。然后你城市字母表遍历的对象就不再是cities,而是letters了(那就不需要使用key了,直接遍历输出item就可以):

{{item}}

接下来继续编写handleTouchMove函数的内容,这个函数就是用于计算当你拇指在滑动的时候,你的拇指停留在哪个字母上。实现的逻辑是先获取A字母距离顶部的高度,然后当你滑动的时候获取你当前字母距离顶部的高度,接着将后者减去前者得到一个差值,最后用这个差值除于每个字母的长度就能得到这是第几个字母。

第一步在template中给DOM节点添加ref属性,用于获取某个DOM节点:ref="item";

第二步,修改handleTouchMove函数为:

   handleTouchMove () {      if (this.touchStatus) {        const startY = this.$refs['A'][0].offsetTop        console.log(startY)      }    },

通过测试发现当你拇指在滑动的时候,控制台始终输出61,这个61就是A字母距离页面顶部(注意是蓝色区域底部)的高度:

1cdec9ae5ea59cf0eee16993cf221756.png

然后就可以获取每个字母距离整个页面的高度,里面有一个最小的值就是拇指距离整个页面最小的高度:

bba3c7f699a7418de50ce4add4c840e4.png

如果你想要获取某个字母距离蓝色区域底部的高度,可以将其高度减去蓝色区域底部的高度79(43(Header组件高度)+36(Search组件告高度)=79),然后除以20(每个字母高度)并向下取整就能得到每个字母的索引:

  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)        console.log(index)      }    },

测试发现拇指移动到字母M处,右侧显示正常:

79a4692fe3ea8cf4612f55d944c83110.png

最后使用子组件Alphabet使用发布订阅模式向City组件传递数据:

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])        }      }    },

测试发现功能显示正常。

城市列表页新能优化

接下来就是对上面的代码进行优化,因为startY是一个定值,而按照目前写的代码则是每次都需要去执行,这会造成性能低下。可以在data中return一个startY(初始值为0),然后定义一个生命周期函数update,只有页面的数据被更新同时页面完成了渲染时,该方法才会被执行:

  data () {    return {      touchStatus: false,      startY: 0    }  },  updated () {    this.startY = this.$refs['A'][0].offsetTop  },handleTouchMove (e) {      if (this.touchStatus) {        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])        }      }    },

我们知道页面一开始的时候cities变量是空值,也就是Alphabet组件内不会显示任何信息,然后通过ajax获取到数据时Alphabet组才会被重新渲染,之后会触发生命周期函数updated,这时候开始计算蓝色区域底部与字母A标签的距离。

还有一个优化就是函数节流。函数节流就是限制一个函数在一定时间内只能执行一次,这里就是当你拇指在字母表中上下移动时,touchmove函数执行的频率非常高会造成性能低下,因此可以借助于函数节流来优化该代码。具体的函数节流介绍可以参看这篇文章JS进阶篇1---函数节流(throttle),此处采用计时器规定在一定时间内函数才允许执行。打开Alphabet.vue文件,先return一个timer对象,初始值是null,其次修改handleTouchMove函数代码为:

handleTouchMove (e) {      if (this.touchStatus) {        if (this.timer) {          clearTimeout(this.timer)        }        this.timer = setInterval(() => {          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])          }        }, 16)      }    },

原理非常简单,先判断timer计时器对象是否存在,存在就清空该计时器避免缓存,否则就定义一个计时器并设置计时器时间为16毫秒,即每16毫秒才计算一次,这样可避免不必要的计算工作。

搜索功能实现

在gitee的分支栏点击新建分支city-search-logic,然后记得将本地master分支切换到city-search-logic分支,接下来开始进行搜索框的业务逻辑开发。

打开city文件夹中的Search.vue组件,修改其中的template代码为:

123

这里面其实就是新增了一个搜索结果展示的区域,也就是.search-content类所占区域,接着在style标签中新增.search-content类所对应的样式(注意它应该和.search类是平级关系):

.search-content  z-index: 1  overflow: hidden  position: absolute  top: 1.58rem  left: 0  right: 0  bottom: 0  background: green

然后需要实现搜索信息与结果展示区域的联动,就需要使用到数据的双向绑定:

最后结果肯定是显示城市,那么需要从父组件City中接收cities,修改City.vue组件中的template代码为:

....

然后在子组件Search.vue中通过props来接收cities:

  props: {    cities: Object  },

然后我们在子组件Search.vue中return一个数组list(该数组用于存储搜索结果)和计时器timer(函数节流使用):

  data () {    return {      keyword: '',      list: [],      timer: null    }  }

接着我们定义一个侦听器用于监听keyword的变化:

  watch: {    keyword () {      if (this.timer) {        clearTimeout(this.timer)      }      this.timer = setInterval(() => {        // 书写在cities对象中查找某个元素的逻辑        const result = []        for (let i in this.cities) {          // i就是字母A,B...,而this.city[i]就是数组,value就是数组中的每一项          this.cities[i].forEach((value) => {            if (value.spell.indexOf(this.keyword) > -1 ||              value.name.indexOf(this.keyword) > -1            ) {              result.push(value)            }          })        }        this.list = result      }, 100)    }  }

在city.json文件中,我们定义的cities结构为:

"cities": {            "A": [{                "id": 56,                "spell": "aba",                "name": "阿坝"            }, {                "id": 57,                "spell": "akesu",                "name": "阿克苏"            }...

也就是cities本身是一个对象,里面又包含了一个数组作为值,而数组中包含的则是一个个对象。注意if语句中的判断条件为value.spell.indexOf(this.keyword) > -1 ||value.name.indexOf(this.keyword) > -1`也就是通过拼音或中文都可以查找是否能找到匹配的数据。

接下来就是对搜索结果的布局进行优化,给搜索结果添加一个.search-item类和一个边框类.border-bottom:

{{ item.name }}

然后给.search-item类添加样式,并修改.search-content类的背景颜色为#eee:

.search-content  z-index: 1  overflow: hidden  position: absolute  top: 1.58rem  left: 0  right: 0  bottom: 0  background: #eee  .search-item    line-height: .62rem    padding-left: .2rem    background: #fff    color: #666

测试发现页面显示正常,但是搜索结果是无法滚动的,此时可以借助于bertter-scroll来实现。

第一步获取到search-content类节点DOM:

  
{{ item.name }}

第二步导入Bscroll类及创建该对象:

只需这两步就完成了页面滚动的效果。还有一个问题就是当你输入完并清空搜索框的时候,搜索结果依旧还是存在:

1bb8abee32d332a6a8bf3a606513b352.png

其实只需要当你的keyword为空的时候,你将这个list设置为[]即可,在侦听器中添加实现上述功能的逻辑:

  watch: {    keyword () {      if (this.timer) {        clearTimeout(this.timer)      }      if (!this.keyword) {        this.list = []        return      }     ...  }

这样就解决了这个问题。新的问题又来了就是当你输入一串非常长的字母或者说是输入的关键词不能匹配任何城市时,前面我们是什么也不显示,其实这个是有一点问题的,我们最好是在页面上添加诸如“找不到对应的城市”等信息。最简单的方式就是使用v-show来进行显示:

 
{{ item.name }} 没有找到匹配的城市

这样我们就实现了当list的长度为0的时候才显示“没有找到匹配的城市”字眼,但是这样会造成一个问题,就是刚开始你不输入关键词的时候也会出现这个“没有找到匹配的城市”字眼,把热门城市和城市列表给遮住了。这个问题还是可以通过v-show来解决(只不过这次将v-show 标签加到search-content类上):

    
{{ item.name }} 没有找到匹配的城市

这样就完美地解决了上述问题。前面介绍过html中最好不需要包含计算逻辑(在“没有找到匹配的城市”标签上使用了取反运算):

没有找到匹配的城市

因此需要将这个取反的逻辑使用计算属性来代替:

  computed: {    hasNoData () {      return !this.list.length    }  },

自然而然就需要修改上面的template中的取反运算代码为:

没有找到匹配的城市

这样就完成了搜索框的业务逻辑。最后就是将我们的代码上传到city-search-logic分支,并且将其与master分支进行合并,相应的步骤如下:

git statusgit add .git commit -m 'search logic finish'git pushgit checkout mastergit merge city-search-logicgit push
c89bb733c76fef22263886b137c5acd1.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/472550.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

写接口是什么意思啊_科普贴:果汁包装上写NFC到底是什么意思?

比起水果&#xff0c;果汁省去了繁杂的处理步骤&#xff0c;选对了能更快地补充营养。今天麦德龙美厨课堂就为大家推荐METRO Chef NFC100%果汁系列&#xff0c;给你真材实料、原汁原味的营养美味。NFC果汁&#xff1a;真材实料 原汁原味如今果汁饮品市场上品类繁杂&#xff0c;…

LeetCode 1802. 有界数组中指定下标处的最大值(思维题)

文章目录1. 题目2. 解题1. 题目 给你三个正整数 n、index 和 maxSum 。 你需要构造一个同时满足下述所有条件的数组 nums&#xff08;下标 从 0 开始 计数&#xff09;&#xff1a; nums.length nnums[i] 是 正整数 &#xff0c;其中 0 < i < nabs(nums[i] - nums[i1]…

Linux单用户能做什么,Linux单用户模式详解 及应用场景

一、单用户模式简介二、进入单用户模式修改密码1、Centos6进入单用户修改密码1、重启系统&#xff0c;进入系统欢迎界面按上下左右键进入GRUB界面&#xff1b;2、在GRUB界面选择内核版本&#xff0c;按下e键&#xff1b;3、在此界面可以进行编辑&#xff0c;在最后输入single再…

简单阻容降压电路图_升压降压芯片电路

升压与降压一般是指电源电路的工作模式&#xff0c;有些电源IC可以同时支持升压和降压模式。降压模式——Bust mode&#xff0c;这个大家比较熟悉的&#xff0c;用的也比较多&#xff0c;比如5V-》3.3V稳压&#xff0c;对应的芯片很多大家上网搜一下就有了&#xff0c;有LDO模式…

linux终端分辨率对应表,Linux下设置终端分辨率,最全的VGA代码和分辨率对照表...

一般安装完linux后默认的kernel给的tty分辨率是非常有限的,解决的方法就是给kernel传递VGA参数。对应的要修改的配置文件为 /etc/grub.conf,(或者 /boot/grub/grub.conf)就是在grub.conf启动列表的kernel最后添加vga参数 vgaxxxx, 这个在网上大家列出的解决方案都是大同小异&…

android文件存储位置切换

最近有个需求&#xff0c;助手的google卫星地图和OpenCycleMap下载的离线地图数据&#xff0c;要能够在内置存储和外置存储空间之间切换&#xff0c;因为离线瓦片数据非常大&#xff0c;很多户外用户希望将这些文件存储在外置TF卡上&#xff0c;不占用内置存储空间&#xff0c;…

[c]扫雷

题目描述 扫雷游戏是一款十分经典的单机小游戏。在n行m列的雷区中有一些格子含有地雷&#xff08;称之为地雷格&#xff09;&#xff0c;其他格子不含地雷&#xff08;称之为非地雷格&#xff09;。 玩家翻开一个非地雷格时&#xff0c;该格将会出现一个数字——提示周围格子中…

netcore docker_让.NetCore程序跑在任何有docker的地方

一.分别在Windows/Mac/Centos上安装DockerWindows上下载地址&#xff1a;https://docs.docker.com/docker-for-windows/install/&#xff08;window上安装的常见问题和解决方案请参考下方步骤六&#xff09;Mac上下载地址&#xff1a;https://hub.docker.com/editions/communit…

MapReduce 编程实践

文章目录1. MapReduce 作业流程2. 实践2.1 启动 hadoop2.2 创建 java 项目2.3 MapReduce shell2.4 MapReduce Web UI3. MapReduce 编程实践&#xff1a;统计对象中的某些属性参考书&#xff1a;《Hadoop大数据原理与应用》1. MapReduce 作业流程 2. 实践 2.1 启动 hadoop sta…

MapReduce 编程实践:统计对象中的某些属性

文章目录1. 生成数据2. 编写实体类3. Mapper类4. Reducer类5. Driver类6. 运行参考书&#xff1a;《Hadoop大数据原理与应用》 相关文章&#xff1a;MapReduce 编程实践 1. 生成数据 超市消费者 数据&#xff1a; id&#xff0c; 时间&#xff0c;消费金额&#xff0c;会员/…

超级签名源码_企业签名和超级签名有哪些区别?

我们知道iOS系统对于非App Store中的应用是有安装限制的&#xff0c;而App Store严格的审核机制又将许多APP拒之门外&#xff0c;这令不少开发者们郁闷不已。所以很多开发者们会选择苹果签名的方式&#xff0c;让自己的iOS APP可以不经过App Store就安装在用户的苹果手机上&…

天池 在线编程 到达终点

文章目录1. 题目2. 解题1. 题目 描述 A robot is located in a pair of integer coordinates (x, y). It must be moved to a location with another set of coordinates. Though the bot can move any number of times, it can only make the following two types of moves:…

第十七节(is-a 、is-like-a 、has-a,包和 import )

is - a 类与类之间的继承关系&#xff1b;is - like - a 类与接口之间的关系&#xff1b;has - a 关联关系&#xff1b; public class Animal{public void method01();}// 类与类之间的关系class Dog extends Animal{ // Dog is a Animal} /// public interface I{public void…

quartz获取开始结束时间_Springboot集成quartz

Quartz 是一个完全由 Java 编写的开源作业调度框架,为在 Java 应用程序中进行作业调度提供了简单却强大的机制。本文描述在springboot 2.x环境下怎么集成quartz。一、添加quartz到项目中在pom.xml中加入 <dependency>特别注意application入口类的注解&#xff0c;这里一定…

linux 添加本地磁盘,XenServer如何添加本地存储

在一次测试中&#xff0c;发现本地有两块磁盘&#xff0c;但是只有一块磁盘在XenServer中显示出来&#xff0c;另外一块没有显示。本地只有一个Local storage。查询KB后&#xff0c;发现XenServer可以添加多块本地存储。详情&#xff0c;请见KB&#xff1a;CTX121313详细添加如…

LeetCode 1805. 字符串中不同整数的数目(哈希set)

文章目录1. 题目2. 解题1. 题目 给你一个字符串 word &#xff0c;该字符串由数字和小写英文字母组成。 请你用空格替换每个不是数字的字符。 例如&#xff0c;“a123bc34d8ef34” 将会变成 " 123 34 8 34" 。 注意&#xff0c;剩下的这些整数间至少要用一个空格隔…

linux 树状结构图,linux下tree指令的用法, 树状图列出目录, 树状图逐级列出目录...

tree命令&#xff0c;主要功能是创建文件列表&#xff0c;将所有文件以树的形式列出来linux下的tree就比较强大了&#xff0c;但一般系统并不自带这个命令&#xff0c;需要手动下载安装,安装sudo apt install tree## or using yum# yum -y install tree语法tree[-aACdDfFgilnNp…

映射表map(平衡二叉树实现)_手动实现Java集合容器之TreeMap(上)

上一篇我们手写了HashMap&#xff0c;还有一个很重要的Map的实现类TreeMap。打开源码第一句话&#xff1a;* A Red-Black tree based {link NavigableMap} implementation.TreeMap是一个基于红黑树的实现。对红黑树没有了解怎么办&#xff0c;那就先搞清楚红黑树的原理。只要理…

wltc循环多少公里_原来所有车都烧机油!但是烧多少才算正常你知道吗?

点击上方“腾讯汽车”关注我们&#xff0c;最新汽车资讯&#xff0c;最方便的用车常识还有最萌的小编都在这里啦&#xff01;最为当今汽车用户最为关心的问题&#xff0c;烧机油现象被很多用户当做引擎是好是坏的重要标准。但根据机油在引擎内冷却及清理引擎内杂质的循环机制来…

LeetCode 1806. 还原排列的最少操作步数(模拟)

文章目录1. 题目2. 解题1. 题目 给你一个偶数 n​​​​​​ &#xff0c;已知存在一个长度为 n 的排列 perm &#xff0c;其中 perm[i] i​&#xff08;下标 从 0 开始 计数&#xff09;。 一步操作中&#xff0c;你将创建一个新数组 arr &#xff0c;对于每个 i &#xff…