Vue.js學習記錄-13-Vue去哪兒網專案實戰:城市列表頁開發-Search + List
-
Search:城市選擇資訊輸入檢索 (增量式)
功能點2:使用者可以在搜尋欄中可輸入資訊進行城市資訊的檢索,檢索結果以列表形式展現,選定城市後會進行首頁的路由跳轉。
功能點2分析:使用者故事角度
作為使用者,我想在搜尋欄中輸入資訊後會有結果資訊以列表形式展現,並且列表內的內容選擇後可以進行頁面的跳轉,依次來實現城市資訊的變更。
具體實現:
元件data初始化:
name: 'CitySearch', props: { cities: Object 父元件City傳遞的城市資訊物件 }, data() { return { keyword: '', 城市名稱關鍵字 list: [], 存放城市名稱資訊搜尋結果 timer: null 定時器變數 } }
父元件通過屬性進行資料傳遞:
<city-search :cities="cities"></city-search>
細節1:搜尋欄需要對關鍵字屬性進行雙向繫結,並實現監聽,當搜尋欄中存在關鍵字會進行下拉列表的展示。
<template>:v-model實現屬性的雙向繫結。
<div class="search"> <input v-model="keyword" class="search-input" type="text" placeholder="輸入城市名或拼音"> </div>
<script>:對keyword進行響應式偵聽,通過定時器來實現在指定時間內進行指定操作。
watch: { keyword() { if (this.timer) { clearTimeout(this.timer) } // 如果關鍵字不存在,清空陣列 if (!this.keyword) { this.list = [] return } this.timer = setTimeout(() => { const result = [] // 迴圈至字母表陣列 for (let i in this.cities) { // 對每個字母表陣列進行迴圈 this.cities[i].forEach((value) => { // 針對item的spell、name進行關鍵字是否為其子串進行判斷 if (value.spell.indexOf(this.keyword) > -1 || value.name.indexOf(this.keyword) > -1) { result.push(value) } }) // 將結果賦值給list陣列 this.list = result } }, 100) } },
細節2:下拉列表的城市資訊展示分兩種:存在城市資訊已列表形式展示,並可以上下滑動/無城市資訊顯示提示資訊。
-
better-scroll 是一款重點解決移動端(已支援 PC)各種滾動場景需求的外掛
-
引入better-scroll
import BScroll from 'better-scroll'
-
在元件例項初始化渲染時,利用鉤子函式進行better-scroll外掛例項初始化
mounted() { // 鉤子函式例項化better-scroll this.scroll = new BScroll(this.$refs.search) }
-
元件例項依賴繫結DOM元素,採用ref進行DOM元素引用
<div class="search-content" ref="search" v-show="keyword">
此外該DOM元素的展示與否,依賴於keyword的是否為空
-
城市資訊列表的佈局:<ul>
<div class="search-content" ref="search" v-show="keyword"> <ul> <li class="search-item border-bottom"> // 城市資訊列表 </li> <li class="search-item border-bottom" v-show="hasNoData"> 沒有找到匹配資料 </li> </ul>
-
-
無城市資訊顯示提示資訊
對上述城市資訊列表佈局中的第二個<li>標籤進行分析:hasNoData控制此標籤元素的展示與否
計算屬性hasNoData的構建:在模板標籤內部不宜出現Js表示式,計算屬性實質是對結果陣列list長度進行判斷。
computed: { hasNoData() { return !this.list.length } },
細節3:對下拉列表的資訊進行點選後,攜帶城市資訊實現頁面跳轉,這裡採用Vuex進行資料共享。
對上述城市資訊列表佈局中的第一個<li>標籤進行分析:
<li v-for="item of list" :key="item.id" @click="handleCityClick(item.name)" class="search-item border-bottom"> {{item.name}} </li>
上述程式碼中老生長談的v-for,key的設定,以及插值表示式展示資料value。我們在這裡就不提了,著重看一下這個點選事件:handleCityClick
在這個handleCityClick方法中,將城市名稱item.name作為引數傳入,結合vuex實現資料共享,也就是我們上篇文章中未提到的元件通過dispatch發起資料排程。
我們詳細的看一下這個handleCityClick方法: 下文是常規的vuex呼叫流程,並未使用vuex的map對映概念
handleCityClick(city) { // 元件通過store提供的dispatch方法,向store觸發事件並攜帶引數 this.$store.dispatch('changeCity', city) // 元件也可以跳過dispatch方法,直接使用store中的commit方法進行事件的觸發 // this.$store.commit('changeCity', city) // 由於採用了keep-alive進行了DOM內容儲存,提升體驗度這裡完成上述業務邏輯後,將搜尋區域下拉框關鍵字置為空,從而控制下拉列表的展示 this.keyword = '' // 點選更改城市資訊之後,實現頁面跳轉 this.$router.push('/') },
-
補充說明:
-
常規流程是 vue component 向 Actions 發起 dispatch ,Actions 向 Mutations 進行 commit,但是vue component 也可以直接向 Mutations 進行 commit。
-
關於keep-alive,後續會提到,這裡是將DOM內容暫存記憶體中,但是這樣會使得搜尋框的下拉列表再次進入City介面時仍保持原狀,影響體驗,於是這裡博主添加了this.keyword = ’ ’ ,目的是通過關鍵字置空從而控制下拉列表的展示。
未新增前頁面下拉列表演示:
-
方法內部實現路由跳轉:例項.$router.push(‘url’) 即可。
-
下述元件的說明中,會採用vuex的高階對映特性,簡化程式碼的書寫。
-
List:城市列表展示 (增量式)
元件data初始化:
name: 'CityList', props: { cities: Object, hot: Array, letter: String },
父元件通過屬性進行資料傳遞:
<city-list :cities="cities" :hot="hotCities" :letter="letter"></city-list>
功能點5:當前城市資訊展示區域的選定城市資訊展示
這裡我們接著上篇文章中完成一半的功能點5繼續往下說明,在這個List元件中,共分為三部分割槽域:當前城市、熱門城市、字母城市列表。
這三部分的樣式設計是一樣的,只是區域下的邏輯各有不同。那麼針對功能點5進行結尾說明:
通過vuex實現資料共享,資料來源資訊儲存使用在store模組中的state中,在獲取資料方面上我們可以通過vue的高階特性對映拿到mapState。
import { mapState, mapMutations } from 'vuex'
這裡引入mapMutations目的是簡化資料互動操作,功能點3上會使用該對映變數。
通過計算屬性進行state資料來源資訊獲取:
computed: { ...mapState({ currentCity: 'city' }) },
這裡的使用方式與上篇文章有所不同,關於變數的命名可以類比ES6語法中,key-value形式中key和value相同時可以簡寫為一個。
模板中使用:
<div class="button">{{this.currentCity}}</div>
功能點3:使用者可以在熱門城市、字母城市列表中選擇城市資訊,選定城市後會進行首頁的路由跳轉。
整個List列表採用了better-scroll的滾動場景佈局,依舊是首先引入better-scroll,通過ref繫結DOM元素例項化BScroll。
// 引入better-scroll外掛 import BScroll from 'better-scroll' // ref繫結DOM元素 <div class="list" ref="wrapper"> // 鉤子函式例項化BScroll物件 // 採用生命週期鉤子函式進行DOM引用獲取 mounted() { // 建立例項 this.scroll = new BScroll(this.$refs.wrapper) },
該功能點涉及的區域為:熱門城市、字母城市列表
請求返回的資料中分別對應:hot、cities
<template>:
<div class="area"> <div class="title border-topbottom">熱門城市</div> <div class="button-list"> <div class="button-wrapper" v-for="item of hot" :key="item.id" @click="handleCityClick(item.name)"> <div class="button">{{item.name}}</div> </div> </div> </div>
熱門城市區域佈局概況:迴圈遍歷接收到的hot陣列,利用插值表示式進行資料展示,此外綁定了點選事件handleCityClick。
<!-- 採用ref進行區塊滾動DOM結構的標識 --> <div class="area" v-for="(item, key) of cities" :key="key" :ref="key"> <div class="title border-topbottom">{{key}}</div> <div class="item-list"> <div class="city border-bottom" v-for="innerItem of item" :key="innerItem.id" @click="handleCityClick(innerItem.name)"> {{innerItem.name}} </div> </div> </div>
字母城市列表佈局概況:由於接受的是Object物件,JSON資料採用了[A]-[A1,A2,…]該型別的包裝,該區域首先迴圈遍歷外側資料作為字母表展示,接著對獲取到的item進行遍歷,遍歷出字母列表下的城市列表內容。此外這裡也綁定了點選事件handleCityClick。
注:這裡的ref="key"綁定了該DOM,是為了實現字母表導航條的隨動效果而新增的,隨動效果對於該區域來講是被動的,我將會在Alphabet元件中進行詳細說明。
<script>:
handleCityClick事件:攜帶城市資訊,使用者點選後,通過vuex實現城市資訊的變更。
上文已經提到了:引入了mapMutations變數進行簡化資料互動操作。
methods: { handleCityClick(city) { // 元件通過store提供的dispatch方法,向store觸發事件並攜帶引數 // this.$store.dispatch('changeCity', city) // 元件也可以跳過dispatch方法,直接使用store中的commit方法進行事件的觸發 // this.$store.commit('changeCity', city) // 使用mapMutations進行方法對映 this.changeCity(city) // 點選更改城市資訊之後,實現頁面跳轉 this.$router.push('/') }, // 對映名稱為changeCity的mutation到元件方法中 ...mapMutations(['changeCity']) },