1. 程式人生 > >Vue.js學習記錄-13-Vue去哪兒網專案實戰:城市列表頁開發-Search + List

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:下拉列表的城市資訊展示分兩種:存在城市資訊已列表形式展示,並可以上下滑動/無城市資訊顯示提示資訊。

    1. better-scroll 是一款重點解決移動端(已支援 PC)各種滾動場景需求的外掛

      1. 引入better-scroll

        import BScroll from 'better-scroll'

      2. 在元件例項初始化渲染時,利用鉤子函式進行better-scroll外掛例項初始化

         mounted() {
             // 鉤子函式例項化better-scroll
             this.scroll = new BScroll(this.$refs.search)
         }
        
      3. 元件例項依賴繫結DOM元素,採用ref進行DOM元素引用

         <div class="search-content" ref="search" v-show="keyword">
        

        此外該DOM元素的展示與否,依賴於keyword的是否為空

      4. 城市資訊列表的佈局:<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>
        
    2. 無城市資訊顯示提示資訊

      對上述城市資訊列表佈局中的第二個<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('/')
          },
    

補充說明:

  1. 常規流程是 vue component 向 Actions 發起 dispatch ,Actions 向 Mutations 進行 commit,但是vue component 也可以直接向 Mutations 進行 commit。

  2. 關於keep-alive,後續會提到,這裡是將DOM內容暫存記憶體中,但是這樣會使得搜尋框的下拉列表再次進入City介面時仍保持原狀,影響體驗,於是這裡博主添加了this.keyword = ’ ’ ,目的是通過關鍵字置空從而控制下拉列表的展示。

    未新增前頁面下拉列表演示:
    在這裡插入圖片描述

  3. 方法內部實現路由跳轉:例項.$router.push(‘url’) 即可。

  4. 下述元件的說明中,會採用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'])
    	},