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) } }可以測試一下當你點擊右側列表中的字母,控制臺是否真的輸出了對應的字母:
前面說了由于這里的業務邏輯不是很復雜,因此可以將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]) } } }當你點擊某個字母時,控制臺輸出:
而這個索引為0的div.area區域中包含了我們所需要的的城市列表信息:
既然這樣就可以直接獲取到對應的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字母距離頁面頂部(注意是藍色區域底部)的高度:
然后就可以獲取每個字母距離整個頁面的高度,里面有一個最小的值就是拇指距離整個頁面最小的高度:
如果你想要獲取某個字母距離藍色區域底部的高度,可以將其高度減去藍色區域底部的高度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處,右側顯示正常:
最后使用子組件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類及創建該對象:
只需這兩步就完成了頁面滾動的效果。還有一個問題就是當你輸入完并清空搜索框的時候,搜索結果依舊還是存在:
其實只需要當你的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總結
以上是生活随笔為你收集整理的listrecord根据某个属性去重_去哪网开发实战记录(9):城市选择页(中)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Python基础知识-pycharm版
- 下一篇: insert和update 锁等待_黑龙