獨立完成一個城市選擇組件(阿裡前端題目,內附知識點、思路)

来源:https://www.cnblogs.com/lunlunshiwo/archive/2018/03/27/8655514.html
-Advertisement-
Play Games

剛纔被博客園官方移出首頁,不服,再發一遍,絕對原創,手打,思路清晰。 首先說一下,我不是阿裡的人,也沒去阿裡面試過,這是某微信群里的一個小伙伴給的,我現在的能力達不到阿裡的要求。不過人沒夢想還不如鹹魚,有能力的話還是想去嘗試一下。本文如有不足,請勿嘲諷,指出不足即可,謝謝。碼字不易,且看且珍惜,轉載 ...


  剛纔被博客園官方移出首頁,不服,再發一遍,絕對原創,手打,思路清晰。

  首先說一下,我不是阿裡的人,也沒去阿裡面試過,這是某微信群里的一個小伙伴給的,我現在的能力達不到阿裡的要求。不過人沒夢想還不如鹹魚,有能力的話還是想去嘗試一下。本文如有不足,請勿嘲諷,指出不足即可,謝謝。碼字不易,且看且珍惜,轉載請註明出處。原創博客,若侵犯貴司的利益,請私信我刪除。若覺得不錯,求個贊和github的star。

  題目如下:

  大概就是這樣吧,分析一下就是做一個城市選擇組件,實現的功能或者要求呢就是可以定位當前的城市、用localstorage存儲上次定位的城市和最近選擇過的城市、可以按照輸入的字母或者文字篩選出想找的城市、將數據帶到頁面也就是一個父子傳參的問題吧、頁面使用flex佈局。

  我在下班閑暇時間簡單的做了一下,成功如下:

  

  我僅僅做了這個組件,向頁面傳參的功能還沒做,可以用父子組件傳參完成。

知識點部分:

  簡單的說一下我這個城市選擇組件和其中的一下知識點:

  1.後臺

    我用node.js起了一個後臺服務,使用的express框架,完成滿足了我的需求。我的數據來源是爬取的某網站的城市地址(若侵權請聯繫我刪除),數據是這樣的:

    {
      "id": 151,
      "name": "鞍山",
      "pinyin": "anshan",
      "acronym": "as",
      "rank": "C",
      "firstChar": "A"
    }

    我在node端調用了某浪的一個定位介面作為我的定位服務,並將數據返回,當這個介面有問題或者沒獲取到的時候會返回定位在北京。具體代碼為:

// 獲取城市數據,city為我爬取的信息
app.get('/', function (req, res) {
    res.send(city);
    res.end()
});
// 調用新浪的介面返回定位
app.get('/nowcity', function (req, res) {
    let getIpInfo = function (cb) {
        var url = 'http://int.dpool.sina.com.cn/iplookup/iplookup.php?format=json';
        http.get(url, function (res) {
            var code = res.statusCode;
            if (code == 200) {
                res.on('data', function (data) {
                    try {
                        cb(JSON.parse(data));
                    } catch (err) {
                        console.log(err)
                    }
                });
            }
        }).on('error',function(e){
            cb({
                city: "北京",
                country: "中國",
                province: "北京",
            })
        })
    };
    getIpInfo(function (msg) {
        let nowcity = msg
        res.send(nowcity)
        res.end()
    })
});

  2.vue腳手架

    本次組件基於vue框架,我使用vue-cli腳手架搭建的,這一塊知識不多做描述,參考我的博客《vue環境搭建與創建第一個vuejs文件》。

  3.stylus

    本次我使用了css預處理程式——stylus。

    在vue-cli中使用stylus首先要安裝依賴npm install stylus --save-dev、npm install stylus-loader --save-dev,然後再文件中使用<style lang="stylus" scoped>即可。

    引入單獨的stylus文件使用@import '~common/stylus/css.styl'。

  4.本次項目的依賴

    本次項目中,除了安裝了有關stylus的依賴我還引入了better-scroll、fastclick、axios這三個依賴。

    better-scroll是我見過的最好的處理移動端滾動的庫了,並且文檔清晰,思路明確。fastclick用於處理移動端click事件300毫秒延遲。至於axios,我想大家都知道,axios 是一個基於 promise 的 HTTP 庫,可以用在瀏覽器和 node.js 中。

  5.vue組件的使用

    本次項目我構建了6個功能組件,分別是搜索框組件、搜索頁組件、定位組件、側邊欄組件、彈窗組件、城市顯示組件。還有倆個基礎組件,分別是滾動組件和城市組件。

    引入城市組件的方法是:

// 先引入文件
import Search from 'components/Search'
import Scroll from 'base/Scroll.vue'
import PositionBox from 'components/PositionBox'
import CityList from 'components/CityList'
import NavList from 'components/NavList'
import MaskBox from 'components/MaskBox'
import SearchList from 'components/SearchList'
// 然後在父組件中註冊
components: {
    'search': Search,
    'scroll': Scroll,
    'position-box': PositionBox,
    'nav-list': NavList,
    'city-list': CityList,
    'mask-box': MaskBox,
    'search-list': SearchList
}
// 使用
<search @txtdata="searchText" :clearText="clearSearch"></search>

  6.父子組件傳參

    父組件向子組件傳參非常簡單,就搜索框組件來說:

    <search @txtdata="searchText" :clearText="clearSearch"></search>

    父組件給子組件傳參只需要:clearText="clearSearch"即可,其中clearSearch為要傳入的信息,clearText為子組件接收的名稱。

    在子組件中,使用props屬性操作傳參:

props: {
    clearText: Boolean
}
// 帶預設參數的
props: {
    clearText: {
      type: Boolean,
      default:false
    }
}

    子組件向父組件傳參使用this.$emit傳參:

// 點擊列表觸發改變定位的事件
this.$emit('txtdata', this.searchText)


    在上面的代碼中txtdata為傳遞到父組件的內容的名字,this.searchText為參數。在父組件端使用@來觸發接收事件@txtdata="searchText"

// 搜索框內容
    searchText (text) {
    // text即傳遞過來的參數
    }

  7.延遲操作

    我們在處理前端的ajax時一般希望減少交互來提高性能和效率。在搜索框組件中,我們使用到了聯想搜索的功能,這裡我使用正則實現的。因此在打字的過程中,我們希望在打字完成菜進行交互(總不能讓瀏覽器一直都在遍曆數組或者Ajax)。在這裡我使用了一個定時函數完成延時效果:

if (this.timer) {
   clearTimeout(this.timer) // 清除定時器
}
this.timer = setTimeout(() => {
   this.$emit('txtdata', this.searchText)
}, 300)

    在這段代碼中,我綁定了keyup事件,也就是說,300毫秒中只要有按鈕彈起,就會觸發事件清除上一個定時器,然後重新生成新的定時器,300毫秒內無輸入則定時器觸發,向父組件傳遞參數。

  8.正則

    話說曾經正則是我最頭疼的事情,直到我有一天耐心的看了許多文檔和博客。

export function getSearchList (text, list) {
  let reg1 = /^\w+$/g //檢測是否為字母
  let reg2 = new RegExp(`^${text}`, 'g') //檢測模板text
  let reg3 = new RegExp('^[\\u4E00-\\u9FFF]{1,}$', 'g') //檢測是否為漢字
  let resList = []
    // 當text為字母時
  if (text.match(reg1)) {
    for (let i = 0, len1 = list.length; i < len1; i++) {
      for (let j = 0, len2 = list[i][1].length; j < len2; j++) {
    // 篩選滿足這個正則的
        if (list[i][1][j].pinyin.match(reg2)) {
          resList.push(list[i][1][j])
        }
      }
    }
  } else {
    // 同上
    if (reg3.test(text)) {
      for (let i = 0, len1 = list.length; i < len1; i++) {
        for (let j = 0, len2 = list[i][1].length; j < len2; j++) {
          if (list[i][1][j].name.match(reg2)) {
            resList.push(list[i][1][j])
          }
        }
      }
    }
  }
  return resList
}

    JavaScript通過內置對象RegExp支持正則表達式,有兩種方式創建正則表達式對象,分別是構造函數var reg=new RegExp('<%[^%>]+%>','g')和字面量var reg=/<%[^%>]%>/g,因為我這次用到了模板語句,就是用了構造函數,最後的g代表全局。

    

//匹配一個字元,這個字元可以是0-9中的任意一個 
var reg1 = /[0123456789]/ 
//匹配一個字元,這個字元可以是0-9中的任意一個 
var reg2 = /[0-9]/ 
//匹配一個字元,這個字元可以是a-z中的任意一個 
var reg3 = /[a-z]/ 
//匹配一個字元,這個字元可以是大寫字母、小寫字母、數字中的任意一個 
var reg3 = /[a-zA-Z0-9]/
//匹配一個字元,這個字元可以是漢字的任意一個 
var reg4 = /[\\u4E00-\\u9FFF]/

     我們還能引入開頭結尾的限制:

^ 以xxx開頭
$ 以xxx結尾
\b 單詞邊界
\B 非單詞邊界

    數量量詞:

字元含義
? 出現零次或一次(最多出現一次)
+ 出現一次或多次(至少出現一次)
* 出現零次或多次(任意次)
{n} 出現n次
{n,m} 出現n到m次
{n,} 至少出現n次

  9.this.$refs

    一般來講,獲取DOM元素,需document.querySelector(".input1")獲取這個dom節點,然後在獲取input1的值。但是用ref綁定之後,我們就不需要在獲取dom節點了,直接在上面的input上綁定input1,然後$refs裡面調用就行。然後在javascript裡面這樣調用:this.$refs.input1  這樣就可以減少獲取dom節點的消耗了。

<div ref="wrapper" class="scroll">
</div>
//  此時this.$refs('wrapper')就代表了這個div

  10.slot

    通過字面意思理解,slot為“插槽,水溝”,大概就是一個安放組件或者dom結構的地方。子組件模板必須包含至少一個 <slot> 插口,否則父組件的內容將會被丟棄。當子組件模板只有一個沒有屬性的插槽時,父組件傳入的整個內容片段將插入到插槽所在的 DOM 位置,並替換掉插槽標簽本身。最初在 <slot> 標簽中的任何內容都被視為備用內容。備用內容在子組件的作用域內編譯,並且只有在宿主元素為空,且沒有要插入的內容時才顯示備用內容。

    假定 my-component 組件有如下模板:

<div>
  <h2>我是子組件的標題</h2>
  <slot>
    只有在沒有要分發的內容時才會顯示。
  </slot>
</div>

    父組件模板:

<div>
  <h1>我是父組件的標題</h1>
  <my-component>
    <p>這是一些初始內容</p>
    <p>這是更多的初始內容</p>
  </my-component>
</div>

    渲染結果:

<div>
  <h1>我是父組件的標題</h1>
  <div>
    <h2>我是子組件的標題</h2>
    <p>這是一些初始內容</p>
    <p>這是更多的初始內容</p>
  </div>
</div>

    本次項目的插槽:

<!--父組件-->
<scroll :data="citylist" ref="suggest" :probeType="3" :listenScroll="true" @distance="distance" @scrollStore="scrollStore">
      <div>
        <position-box :chooseCity="chooseCity" :orientate="nowCity" :historyCityArr="historyCityArr" @changeCity="changeCity"></position-box>
        <city-list :citylist="citylist" :elementIndex="elementIndex" @positionCity="changeCity" @singleLetter="singleLetter"></city-list>
      </div>
</scroll>
<!--子組件-->
<div ref="wrapper" class="scroll">
    <slot></slot>
</div>

  11.better-scroll的使用

this.scroll = new BScroll(this.$refs.wrapper, {
        probeType: this.probeType,
        scrollY: true, // 滾動方向為Y軸
        click: true, // 是否派發click事件,通常判斷瀏覽器派發的click還是betterscroll派發的click,可以用event._constructed,若是bs派發的則為true
        momentum: true, // 當快速滑動時是否開啟滑動慣性
        bounce: false, // 是否啟用回彈動畫效果
        bounceTime: 700, // 彈力動畫持續的毫秒數
        deceleration: 0.001, // 滾動動量減速越大越快,建議不大於0.01
        momentumLimitTime: 300, // 符合慣性拖動的最大時間
        momentumLimitDistance: 15, // 符合慣性拖動的最小拖動距離
        resizePolling: 60 // 重新調整視窗大小時,重新計算better-scroll的時間間隔
})

    通過構建一個scroll對象來使用better-scroll,這裡必須綁定一個dom節點,即this.$refs.wrapper。裡面添加一些屬性來自定義。

    在本次項目中,我們使用了Bscroll的三個方法:

    refresh()

      參數:無

      返回值:無

      作用:重新計算 better-scroll,當 DOM 結構發生變化的時候務必要調用確保滾動的效果正常。

    scrollTo(x, y, time, easing)

      參數:返回值:無

      {Number} x 橫軸坐標(單位 px)

      {Number} y 縱軸坐標(單位 px)

      {Number} time 滾動動畫執行的時長(單位 ms)

      {Object} easing 緩動函數,一般不建議修改,如果想修改,參考源碼中的 ease.js 里的寫法

      作用:滾動到指定的位置

    scrollToElement(el, time, offsetX, offsetY, easing)

      參數:返回值:無

      {DOM | String} el 滾動到的目標元素, 如果是字元串,則內部會嘗試調用 querySelector 轉換成 DOM 對象。(此處我使用了this.$refs)

      {Number} time 滾動動畫執行的時長(單位 ms)

      {Number | Boolean} offsetX 相對於目標元素的橫軸偏移量,如果設置為 true,則滾到目標元素的中心位置

      {Number | Boolean} offsetY 相對於目標元素的縱軸偏移量,如果設置為 true,則滾到目標元素的中心位置

      {Object} easing 緩動函數,一般不建議修改,如果想修改,參考源碼中的 ease.js 里的寫法

      作用:滾動到指定的目標元素。

  12.localstorage

    我相信大家對localstorage和sessionstorage的區別已經都懂了,其最大的區別就是localstorage像ROM,而sessionstorage像RAM。

    在本次項目中,通過setItem和getItem來操作localstorage:

localStorage.setItem('historyCityArr', arr)
localStorage.getItem('historyCityArr')

  13.過渡transition

    類似於在單位渲染和移除的時候添加一個動畫特效。

    <transition name="flag">
      <div class="nowFlag" v-if="flag">{{flagText}}</div>
    </transition>
  .flag-leave-active
    transition all 1s
  .flag-leave-to
    opacity 0

    對於至一段的解釋為,添加一個離開(移除)的過渡,一秒鐘內不透明度由1變成0。

  14.stop&prevent

    在事件處理程式中調用 event.preventDefault() 或 event.stopPropagation() 是非常常見的需求。儘管我們可以在方法中輕鬆實現這點,但更好的方式是:方法只有純粹的數據邏輯,而不是去處理 DOM 事件細節。為瞭解決這個問題,Vue.js 為 v-on 提供了事件修飾符。之前提過,修飾符是由點開頭的指令尾碼來表示的。

    .stop 阻止事件冒泡

    .prevent 阻止預設事件

    .capture 阻止事件捕獲

    .once 只觸發一次

業務部分:

  1.搜索框組件

    html代碼如下:父組件向子組件傳遞是否清空內容的信息(用於點擊搜索頁選項後更改搜索頁),子組件觸發keyup事件時向父組件傳遞需要搜索的內容。

<!--父組件-->
<search @txtdata="searchText" :clearText="clearSearch"></search>
<!--子組件-->
<div class="search-box">
    <div class="ipt-box">
      <input type="text" class="ipt" placeholder="城市名稱/拼音" @keydown="entry()" v-model="searchText" />
      <div class="icon-box">
        <i class="iconfont icon-sousuo icon"></i>
      </div>
    </div>
</div>
//子組件js  
methods: {
    // 延時搜索
    entry () {
      if (this.timer) {
        clearTimeout(this.timer)
      }
      this.timer = setTimeout(() => {
        this.$emit('txtdata', this.searchText)
      }, 300)
    }
  },
  watch: {
    // 清除搜索內容
    clearText (val) {
      if (val) {
        this.searchText = ''
        this.entry()
      }
    }
  }

    在向上傳遞時有一個減少交互和運算的效果,用定時器實現的,上文有講到。

  2.定位組件

<!--父組件模塊-->
<position-box :chooseCity="chooseCity" :orientate="nowCity" :historyCityArr="historyCityArr" @changeCity="changeCity"></position-box>
<!--子組件模塊-->
  <div class="position-box">
    <div class="choose">
      <span>你已選擇:{{chooseCity}}</span>
    </div>
    <div class="hostory">
      <p>定位/最近訪問</p>
      <div class="citybox">
        <button @click="changeCity(orientate)">
          <i class="iconfont icon-dingwei icon"></i>{{orientate}}
        </button>
        <button @click="changeCity(item)" v-for="item in historyCityArr" :key="item">{{item}}</button>
      </div>
    </div>
    <div class="hot">
      <p>熱門城市</p>
      <div class="citybox">
        <button v-for="city in hotCitys" :key="city" @click="changeCity(city)">{{city}}</button>
      </div>
    </div>
  </div>

    在這一部分裡面,一開始載入頁面的時候會觸發兩個事件:定位和讀取localstorage裡面存儲的歷史查看的記錄。

    axios.get('http://localhost:1234/nowcity').then((res) => {
        this.nowCity = res.data.city
        if (!this.choiceCity && !this.choiceCityName) {
          this.choiceCity = this.nowCity
          this.choiceCityName = this.nowCity
        }
      }, () => {
        this.nowCity = '北京'
        if (!this.choiceCity && !this.choiceCityName) {
          this.choiceCity = this.nowCity
          this.choiceCityName = this.nowCity
        }
      })

    定位部分邏輯簡單,無非就是獲取數據,如果獲取不到預設為北京。

    localstorage的數據處理就在這個組件中:

    setHistory (arr) {
      localStorage.setItem('historyCityArr', arr)
    },
    // 從本地取
    getHistory () {
      let history = localStorage.getItem('historyCityArr')
      if (!history) {
        this.historyCityArr = []
      } else {
        this.historyCityArr = history.split(',')
      }
    },
    // 存到本地,正在查看的城市
    setCity (name) {
      localStorage.setItem('seeCity', name)
    },
    // 從本地取,,正在查看的城市
    getCity () {
      let name = localStorage.getItem('seeCity')
      if (!name) {
        this.choiceCity = ''
        this.choiceCityName = ''
      } else {
        this.choiceCity = name
        this.choiceCityName = name
      }
    }

    當查看到城市發生變化時,出觸發兩個setItem事件(無論是存數組還是字元串),以便於在此打開時getItem可以獲取到數據。一開始載入頁面時,會發兩個get事件,獲取到數據之後傳入定位模塊中渲染數據。get得到的信息是字元串,我們獲取到之後要轉轉化為數組。

  3.頁面城市組件

<!--父組件模塊-->
<city-list :citylist="citylist" :elementIndex="elementIndex" @positionCity="changeCity" @singleLetter="singleLetter"></city-list>
<!--子組件模塊-->
<div class="lists">
    <div v-for="citys in citylist" :key="citys[0]" :dataNum="citys[1].length">
      <p class="city-title" :ref="citys[0]">{{citys[0]}}</p>
      <p class="city-item" v-for="city in citys[1]" :key="city.id" @click="changeCity(city.name)">{{city.name}}</p>
    </div>
  </div>

    單說這個組件呢,屬於很簡單的那種,僅僅有展示渲染信息和點擊城市選項向上傳遞城市信息值的功能。但是後面增加了右邊欄nav之後又增加了向上傳遞dom節點的功能:

// 父組件
singleLetter (dom) {
   this.$refs.suggest.scrollToElement(dom, 200, false, false)
}
// 子組件
elementIndex (val) {
   if (val === '頂') {
     return false
   }
   this.$emit('singleLetter', this.$refs[val][0])
}

    父組件獲取到城市組件上傳的城市dom節點信息之後觸發Bscroll的scrollToElement方法,0.2秒內滾動到相應位置。

  4.彈窗組件

    這個組件為點擊選擇城市之後(並且點擊的城市不是當前已經查看的城市)觸發。

<!--父組件模塊-->
<mask-box v-if="maskShow" :message="maskMessage" @chooseing="chooseResult"></mask-box>
<!--子組件模塊-->
<div class="mask-box">
    <div class="mask-body"></div>
    <div class="btn-box">
      <div class="message">
        <p>{{message}}</p>
      </div>
      <div class="btn-left" @click="chooseTrue()">
        <p>確定</p>
      </div>
      <div class="btn-right" @click="chooseFalse()">
        <p>取消</p>
      </div>
    </div>
  </div>

 

    js部分非常簡單

  chooseTrue () {
      this.$emit('chooseing', true)
    },
    chooseFalse () {
      this.$emit('chooseing', false)
    }

    根據點擊的按鈕的不同向上傳值。當傳值為true時觸發父組件一個事件,讓頁面滾動到頂部。

// 是否確認切換定位
    chooseResult (res) {
      if (!res) {
        this.maskClose() // 不切換,僅關閉彈窗
      } else {
        this.choiceCityName = this.choiceCity
        this.local()
        this.associationShow = false // 關閉搜索框(在搜索狀態下)
        this.clearSearch = true // 清除輸入框的字(在搜索狀態下)
        // 當確認後滾動到頂部
        this.$refs.suggest.scrollTo(0, 0, 200)
        this.maskClose()
      }
    }

  5.搜索列表組件

    這個組件頁面代碼不過,邏輯代碼也比較簡單,用到了上文的正則,不多做解釋。

<!--父組件模塊-->
    <transition name="list">
      <search-list v-if="associationShow" :searchListContent="searchListContent" @changeName="changeCity"></search-list>
    </transition>
<!--子組件模塊-->
  <div class="listbody">
    <scroll :data="searchListContent">
      <div>
        <city-item :searchListContent="searchListContent" @changeName="changeCity"></city-item>
      </div>
    </scroll>
  </div>

    組件僅作展示和點擊選擇城市,功能與3組件相同,但是沒有Bscroll的滾動事件。

  6.右邊欄nav組件

<!--父組件模塊-->
<nav-list :navList="cityIndexList" @toElement="toElement" :flagText="flagText"></nav-list>
<!--子組件模塊-->
<div class="navbody">
   <div class="navList" @touchstart.stop.prevent="start" @touchmove.stop.prevent="move">
      <div :class="navClass(item)" :data-name="item" v-for="item in navList" :key="item">
        {{item}}
      </div>
   < /div>
</div>

    這部分html代碼量比較少,但是與其他組件的聯動最多,比如點擊nav上的字母使頁面城市組件滾動到相應的位置了、在上面滑動實現頁面城市組件的持續滾動等。

    在點擊nav上的字母使頁面城市組件滾動到相應的位置這個功能中,點擊觸發了touchstart這個事件:

    start (e) {
      let item = handleDomData(e.target, 'data-name')
      this.touch.start = e.touches[0].pageY
      this.touch.startIndex = getIndex(this.navList, item)
      this.scrollToElement(item)
    }

    記錄第一次點擊的位置為以後的滑動提供起點的高度,並且觸發scrollToElement事件,向上傳值,讓父組件的scroll滾動到相應的位置。

    在滑動實現頁面城市組件的持續滾動這個功能在,觸發touchmove這個事件:

    move (e) {
      this.touch.end = e.touches[0].pageY
      let distance = this.touch.end - this.touch.start
      this.touch.endIndex = Math.min(Math.max(this.touch.startIndex + Math.floor((distance + 10) / 20), 0), 22)
      this.scrollToElement(this.navList[this.touch.endIndex])
    }

    通過滾動過程中的距離量計算當前所處的字母,並上傳改字母,讓父組件的scroll滾動到相應的位置。

    在這個組件中,我們引入了兩個js函數,分別是start中的handleDomData和getIndex

// 獲取或者給dom屬性賦值
export function handleDomData (el, name, val) {
  if (val) {
    return el.setAttribute(name, val)
  } else {
    return el.getAttribute(name)
  }
}
// 獲取每一個字母在數組中對應的index
export function getIndex (arr, query) {
  let key
  arr.map((val, index) => {
    if (val === query) {
      key = index
      return false
    }
  })
  return key
}

  7.(非組件)字母顯示卡片

    這個小東西不是一個組件,但是有一定的功能,因此放在了這裡。代碼超簡單,就是接受兩個參數,是否顯示和顯示啥:

    <transition name="flag">
      <div class="nowFlag" v-if="flag">{{flagText}}</div>
    </transition>

    是否顯示這個參數來自與scroll基礎組件的三個事件:

      // 監聽scroll事件
      if (this.listenScroll) {
        // 滾動開始時觸發
        this.scroll.on('scrollStart', () => {
          this.$emit('scrollStore', true)
        })
        // pos為位置參數
        this.scroll.on('scroll', (pos) => {
          this.$emit('distance', Math.abs(pos.y))
          this.$emit('scrollStore', true)
        })
        // 滾動結束
        this.scroll.on('scrollEnd', () => {
          this.$emit('scrollStore', false)
        })
      }

    this.listenScroll這個參數我們在搜索列表上不調用,因此預設為false,只有在主頁面時傳true。觸發時監聽scroll組件的活動情況,比如滾動開始時上傳true,正在滾動中傳true,結束時傳false來控制卡片的顯示與隱藏。

    卡片上面的字時根據滾動到的距離計算得出的:

    // 根據滑動距離顯示字母牌上的字
    distance (val) {
      for (let i = 0, len = this.arrHeight.length; i < len; i++) {
        if (val < this.arrHeight[i]) {
          this.flagText = this.cityIndexList[i]
          return false
        }
      }
    }
    // 高度數組來源
    // 計算鏈接每一部分的高度
    export function getDistance (arr) {
      let titleHeight = 30
      let itemHeight = 35
      let distanceArr = []
      arr.map((item) => {
        distanceArr.push(titleHeight + itemHeight * item[1].length)
      })
      return distanceArr
    }

    得到的字母除了在這個卡片使用還會傳入navList組件中,實現當前所處字母的樣式的區別。

總結:

    感覺寫的腦袋疼,這個城市選擇組件的形式被應用於各種app和網站,是繼省市二級聯動之後城市選擇功能的實現形式。邏輯頗多,大多在上面被提到。

    項目也上傳github,地址為:https://github.com/lunlunshiwo/ChooseCity(另還有無側欄直接滾動版版:https://github.com/lunlunshiwo/Choose-City-no-nav-list-,使用方法為替換相應文件即可)。使用方式為先用node起一個基於express的服務(指令為——node .\playDate.js,下載位置:https://github.com/lunlunshiwo/ChooseCityServe),再運行vue-cli(指令為npm run dev)。

    至於如何起兩個服務,自行參考cmd和power shell。

 

    碼字不易,且看且珍惜。

    原創博客,若侵犯貴司的利益,請私信我刪除。

    若覺得不錯,求個贊和github的star。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、首先配置redis的主從同步集群。 1、主庫的配置文件不用修改,從庫的配置文件只需增加一行,說明主庫的IP埠。如果需要驗證的,也要加多一行,認證密碼。 slaveof 192.168.20.26 5268 masterauth hodge01 一主多從的話,就啟用多個從庫。其中,從庫都是一樣的 ...
  • 預設情況下, MySQL啟用自動提交模式(變數autocommit為ON)。這意味著, 只要你執行DML操作的語句,MySQL會立即隱式提交事務(Implicit Commit)。這個跟SQL Server基本是類似的。如果你瞭解SQL Server資料庫的話。 查看autocommit模式 由於變... ...
  • 在開發通用型應用的時候,你總是需要記住,iPad並不是一個大大的iPod touch,為iPad開發的應用的界面應該更好地利用iPad的大屏幕,而不應該是iPhone應用的複製品。 參考資料:《iOS編程指南》 ...
  • 這裡說一個官方推薦的寫法: 根據上面的代碼,一旦綁定activity,也自然會註冊介面,所以只要在activity中實現介面便可以了。 ...
  • 動畫 1.常規動畫屬性設置(可以同時選擇多個進行設置) UIViewAnimationOptionLayoutSubviews:動畫過程中保證子視圖跟隨運動。**提交動畫的時候佈局子控制項,表示子控制項將和父控制項一同動畫。** UIViewAnimationOptionAllowUserInteract ...
  • 先看看效果圖 一個消息列表,然後點擊item後會刷新時間,點擊置頂後也會刷新時間,置頂規則就是根據兩個欄位來排序 如果是置頂狀態,top為1,然後每次操作都會刷新 time的時間,time是存的時間戳,先看看實體類 package com.fragmentapp.home.bean; import ...
  • 最近在項目中經常用到UITableView中的cell中帶有UITextField或UITextView的情況,然後在這種場景下,當我們點擊屏幕較下方的cell進行編輯時,這時候鍵盤彈出來會出現遮擋待輸入的cell,導致我們無法很方便地查看到我們輸入的內容,這樣的體驗是非常不好的。這個問題在之前我們 ...
  • HTML5 是下一代的 HTML。 什麼是 HTML5? HTML5 將成為 HTML、XHTML 以及 HTML DOM 的新標準。 HTML 的上一個版本誕生於 1999 年。自從那以後,Web 世界已經經歷了巨變。 HTML5 仍處於完善之中。然而,大部分現代瀏覽器已經具備了某些 HTML5 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...