借用了兩個久經考驗的輪子:fastClick和better-scroll,介意可以就此打住。本文絕對原創,手打,思路清晰,知識不難,不適合大佬觀看,謝謝。
首先說一下,我不是阿里的人,也沒去阿里面試過,這是某微信群裡的一個小夥伴給的,我現在的能力達不到阿里的要求。不過人沒夢想還不如鹹魚,有能力的話還是想去嘗試一下。本文如有不足,請勿嘲諷,指出不足即可,謝謝。碼字不易,且看且珍惜,轉載請註明出處。原創部落格,若侵犯貴司的利益,請私信我刪除。若覺得不錯,求個贊和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傳參:
// 點選列表觸發改變定位的事件
複製程式碼
複製程式碼
在上面的程式碼中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,地址為:github.com/lunlunshiwo…(另還有無側欄直接滾動版版:github.com/lunlunshiwo…,使用方法為替換相應檔案即可)。使用方式為先用node起一個基於express的服務(指令為——node .\playDate.js,下載位置:github.com/lunlunshiwo…),再執行vue-cli(指令為npm run dev)。
至於如何起兩個服務,自行參考cmd和power shell。
碼字不易,且看且珍惜。
原創部落格,若侵犯貴司的利益,請私信我刪除。
若覺得不錯,求個贊和github的star。