城市列表页面的数据渲染

1. 创建新的分支city-ajax:
下载city.json文件,然后保存到mock下。
城市列表页面的数据渲染
同理,Ajax请求我们一般会放在最外层的City组件上来做:

  • 首先引入axios
    城市列表页面的数据渲染
  • 利用mounted函数,执行一个getCityInfo方法:
    城市列表页面的数据渲染
  • methods里定义这个方法
    发起ajax请求,然后触发一个handleGetCityInfoSucc方法,接收到的是模拟数据的结果res
    城市列表页面的数据渲染
  • 接下来就可以做动态数据的渲染了:
    城市列表页面的数据渲染
  • ajax获取到数据后,我们这样写:
    城市列表页面的数据渲染
  • 然后把根组件中的数据传递给子组件List
    城市列表页面的数据渲染
  • List组件中进行接收:
    对热门城市进行处理
    城市列表页面的数据渲染
    效果:
    城市列表页面的数据渲染
    城市首字母处理:
    将多余的代码删掉,只剩下一个area区域,循环area的时候,循环的数据ct是一个对象,所以与item对应的是key,内部再嵌套一个小循环:
    城市列表页面的数据渲染
    就可以实现滑动显示所有城市了。
  • 修改右侧城市首字母,在根组件City中传allcities给子组件Alphabet
    城市列表页面的数据渲染
    在子组件中接收,类型是一个Object
    城市列表页面的数据渲染
    做循环,循环的是一个li标签:
    城市列表页面的数据渲染
    最终的效果:
    城市列表页面的数据渲染
    2. 提交到线上

3. 兄弟组件联动

我们要实现点击右侧的字母,会自动的滚动到对应城市区域,即按首字母索引。

  1. 创建项目分支city-components
  2. 在alphabet组件中编辑:
    给每一个循环绑定一个事件handleLetterClick
    城市列表页面的数据渲染
    methods中定义事件触发该事件时,会接收到一个e,打印e.target.innerTextjs中事件是会冒泡的,所以this是可以变化的,但event.target不会变化,它永远是直接接受事件的目标DOM元素:
    城市列表页面的数据渲染
    可见打印出来的是点击的key值。
  3. 希望将这个值传递给兄弟组件List组件,转到合适的区域。我们不采用总线机制,将这个值首先传递给父组件City组件,然后再由父组件传递给List组件:
    当点击字母时,向外发布一个change事件(自己起的名),携带的内容为e.target.innerText
    城市列表页面的数据渲染
    父组件City监听这个change事件:
    城市列表页面的数据渲染
    methods中定义handleLetterChange,它用letter接收,其实就是e.target.innerText,打印出letter
    城市列表页面的数据渲染
    接下来要将数据传递给子组件List,在City中定义一个数据sigletter默认值为空字符串:
    城市列表页面的数据渲染
    City组件接收到外部传来的letter值后,我们令this.sigletter=letter:
    城市列表页面的数据渲染
    最后只需要传递给子组件List就可以了:
    城市列表页面的数据渲染

4. List组件逻辑编写:
打开List组件,接收父组件传递过来的let,其类型为string
城市列表页面的数据渲染
接下来要做的事就是当子组件List发现let有改变的时候,就让自己的显示区域跳转到和let对应的首字母开头的城市区域显示出来,借助侦听器watch,在watch中侦听let的变化,点击的时候打印出let
城市列表页面的数据渲染
当我们侦听到let的变化时,且let不为空的时候,让better-scroll这个滚动区自动的滚动到某个区域,根据外部传入的let决定。给每一个area加一个ref,也等于key,这个key值就代表ABCD等字母:
城市列表页面的数据渲染
接下来我们就可以由this.refs[this.let]获取到class=“area”的这样一个div区域:
城市列表页面的数据渲染
这样写会报错,因为我们获取的ref是通过循环输出的ref,这样获得的element是一个数组如下:
城市列表页面的数据渲染
但是better-scroll里面我们要求必须是个dom元素,所以这样写:
城市列表页面的数据渲染
至此,就可以实现根据首字母来转换对应的城市区域了。

5. 我们要是实现在右侧字母表上进行上下拖拽时,也会引起左侧城市区域变动:

  • 要做一个右侧字母的滚动监听,打开组件Alphabet,让该组件绑定三个touch事件,并在methods中定义各个事件,我们希望在touchstart之后才触发touchmove,进而触发touchend,所以在data里定义一个标识位,默认false
    城市列表页面的数据渲染
    我们首先获得“A” 这个字母距离顶部的一个高度l1,然后滑动的时候获得手指距离顶部的一个高度l2,用l2减去l1就获得了手指滑动的位置距离字母“A”顶部的距离,在除以每个字母的高度,就知道当前的位置是第几个字母了,然后去取对应的字母,触发一个change事件给外部。想要根据下标找到对应的字母的话,需要一个数组来存储这个字母的列表,定义一个数组类型的数据:
    城市列表页面的数据渲染
    我们就构建出一个letters的计算属性,返回的结果大概是[“A”, “B”, “C”]这样的数组。这一步其实就是将ct里的数据转化成了数组类型。接下来就要修改一下循环了,将循环对象的形式改为循环letters数组的形式:
    城市列表页面的数据渲染
  • 完成拖动
    给每一个li标签也加一个ref
    城市列表页面的数据渲染

计算出“A”这个字母距离蓝色区域底部的距离,offsetTop检测距离父盒子有定位的上面的距离,可见在右侧滚动的时候,距离一直为74
城市列表页面的数据渲染
执行handleTouchMove的时候,我们会接收到一个参数e,事件对象中会有一个touches的数组,第0项表示我们手指的一些信息,可以获取到手指的clientY的值,即手指距离浏览器可视区域最顶部的一个高度:
城市列表页面的数据渲染
蓝色区域的的高度大约为79像素,所以要计算手指离蓝色区域底部的高度,这样写:
城市列表页面的数据渲染
城市列表页面的数据渲染
接下来定义一个值作为手指所在当前字母的下标,算出差值再除以字母的高度,在向下取整,然后设置条件,向外触发一个change事件,父组件监听,并跳转到相应的区域:
城市列表页面的数据渲染
6. 城市列表性能优化

  • 我们定义个一个handleTouchMove方法,当我们的手指在字母表上滑动时,这个方法就会执行,但是这样写性能是比较低的,首先,字母“A”的offsetTop是一个固定的值,在我们每次触动这个方法的时候都会去运算一次,这样使得性能比较低。我们可以这样修改:在data中再定义一个变量,startY:0
    城市列表页面的数据渲染
    然后利用updated生命周期钩子,当数据进行更新且页面完成了渲染的时候,updated就会执行:
    城市列表页面的数据渲染
    接下来就可以修改handleTouchMove中的内容,把对应的startY都换成this.startY
    城市列表页面的数据渲染
  • 函数节流,当我们划动字母表的时候,频率是非常高的,通过节流限制一下函数执行的频率,函数节流的基本思想是设置一个定时器,在指定时间间隔内运行代码时清除上一次的定时器,并设置另一个定时器,直到函数请求停止并超过时间间隔才会执行。在数据中定义一个timer,默认值为null
    城市列表页面的数据渲染
    如果this.timer不为零,清除定时器:
    城市列表页面的数据渲染
    否则,创建一个timer,等于一个定时器,给一个16ms的时间间隔:
    城市列表页面的数据渲染
    把相应的代码放入到定时器当中。

7. 搜索功能实现

  • 创建git分支city-search-logic,然后拉到线下,重启服务器。
    我们希望达到的效果是当在搜索框里搜索城市时,会将我们搜索的城市显示出来,打开Search组件,修改模板,在最外层包裹一层div
    城市列表页面的数据渲染
  • 下面对search-content做布局:
    z-index属性指定了元素及其子元素的【z顺序】,而【z顺序】可以决定当元素发生覆盖的时候,哪个元素在上面。通常一个较大的z-index值的元素会覆盖较低的那一个。
    城市列表页面的数据渲染
    我们希望搜索的内容展示在这个绿色的区块里,要将input搜索框里的搜索词和我们的数据做一个绑定,逻辑里定义一个datadata中存一个数据keyword,默认值为空,现在要和input框做一个双向绑定:
    城市列表页面的数据渲染
    Search这个组件还要接收到City组件传递过来的一个数据ctSearch组件进行接收,类型为Object
    城市列表页面的数据渲染
    然后在data中定义一个空数组list,创建一个侦听器,监听keyword的改变,在这里同样做一个节流函数,定义一个timerkeyword发生改变的时候,延时100毫秒后我们的箭头函数会执行,将输入的字母或者是关键词匹配到相应的城市名,然后pushlist中:
    城市列表页面的数据渲染
    this.list就存储了包含我们关键词keyword的所有城市名称,接下来就可以在列表中做循环了:
    城市列表页面的数据渲染
    接下来对样式进行一个修饰:
    城市列表页面的数据渲染
    当我们输入a时,出现的城市太多,没办法滚动,这时可以还是用better-scroll,先引入better-scroll,借助生命周期函数mounted
    城市列表页面的数据渲染
    然后传入一个dom元素,必须是外层包裹你要滚动区域的元素,利用ref
    城市列表页面的数据渲染
    这样当城市显示过多的时候就可以正常滚动了。

8. 搜索优化

  • 有这样一种情况当我们把搜索框中的“a”,会发删除,发现下面的列表依然还在:
    城市列表页面的数据渲染
    在对keyword进行监听的逻辑代码中我们加一条判断语句,一定要记得return
    城市列表页面的数据渲染
    就可以修复这种bug了。
  • 当我们在搜索框中输入一长串keyword时,并没有匹配上的时候,list当中会什么也不显示:
    城市列表页面的数据渲染
    li标签下增加一条li标签,样式一样,输出信息“没有找到匹配数据!”,但是出现的bug是,当我们输入的关键词很少的时候,也会显示出这条警示:
    城市列表页面的数据渲染
    我们用v-show来限制这条警示语句的显示,即当list的长度为零的时候显示这条警示:
    城市列表页面的数据渲染
  • 解决search-content一直显示的问题,我们希望当我们点击搜索框,即输入keyword的时候这个页面才出现,用v-show来限制它的显示,这样在一开始就又恢复到最初的界面了:
    城市列表页面的数据渲染
  • 我们可以发现在显示警示语句的时候,我们在v-show当中加了逻辑符号“!”,在模板中写逻辑是不提倡的:
    城市列表页面的数据渲染
    我们可以定义一个计算属性:
    城市列表页面的数据渲染
    这样我们就把一些运算放在逻辑区域执行,而在模板中只需要接收数据就好:
    城市列表页面的数据渲染
    最后给li标签里面的循环加上key值。

9. 最后将代码提交到线上。