城市列表页面的数据渲染
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. 兄弟组件联动
我们要实现点击右侧的字母,会自动的滚动到对应城市区域,即按首字母索引。
- 创建项目分支
city-components
: - 在alphabet组件中编辑:
给每一个循环绑定一个事件handleLetterClick
:
在methods
中定义事件触发该事件时,会接收到一个e
,打印e.target.innerText
。js
中事件是会冒泡的,所以this
是可以变化的,但event.target
不会变化,它永远是直接接受事件的目标DOM
元素:
可见打印出来的是点击的key
值。 - 希望将这个值传递给兄弟组件
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
数组的形式: - 完成拖动
给每一个l
i标签也加一个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
搜索框里的搜索词和我们的数据做一个绑定,逻辑里定义一个data
,data
中存一个数据keyword
,默认值为空,现在要和input
框做一个双向绑定:Search
这个组件还要接收到City
组件传递过来的一个数据ct
,Search
组件进行接收,类型为Object
:
然后在data
中定义一个空数组list
,创建一个侦听器,监听keyword
的改变,在这里同样做一个节流函数,定义一个timer
,keyword
发生改变的时候,延时100毫秒后我们的箭头函数会执行,将输入的字母或者是关键词匹配到相应的城市名,然后push
到list
中: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
标签里面的循环加上ke
y值。
9. 最后将代码提交到线上。