(1)在router路径下的index.js文件中,配置city页面的路由
import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/pages/home/Home'
import City from '@/pages/city/City'
Vue.use(Router)
export default new Router({
routes: [{
path: '/',
name: 'Home',
component: Home
}, {
path: '/city',
name: 'City',
component: City
}],
})
(2)创建city组件
(3)在header组件中,点击city跳转到city页面
<router-link to='/city'>
<div class="header-right">
{{this.city}}
<span class="iconfont arrow-icon">span>
div>
router-link>
<template>
<div>
<div class="search">
<input class="search-input" type="text" placeholder="输入城市名或拼音" />
div>
div>
template>
布局如下:
box-sizing: border-box,告诉浏览器设置的border和padding的值是包含在width内的
(1)安装BetterScroll插件
npm install better-scroll --save
(2)插件的使用方式,组件的dom结构必须符合规定要求
<div class="wrapper">
<ul class="content">
<li>...li>
<li>...li>
...
ul>
div>
(3)使用插件
<script>
import Bscroll from 'better-scroll' // 步骤一
export default{
name: 'CityList',
mounted(){
this.scroll = new Bscroll(this.$refs.wrapper); // 步骤二
}
}
</script>
处理点击字母的事件
(1)在Alphabet组件中,点击字母,向city父组件触发change事件,并传递字母数据
handleLetterClick(e) {
// 触发change事件,该事件在City.vue中被监听
this.$emit('change', e.target.innerText)
}
(2)通过city父组件将其传递给list组件
<template>
<div>
<city-header>city-header>
<city-search :cities="cities">city-search>
<city-list
:cities="cities"
:hot="hotCities"
:letter="letter"
>city-list>
<city-alphabet
:cities="cities"
@change="handleLetterChange"
>city-alphabet>
div>
template>
handleLetterChange(letter) {
this.letter = letter
}
(3)在list组件中,监听letter,获取字母对应的区域的元素,通过better-scroll插件,滚动到对应区域。
<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="item border-bottom"
v-for="innerItem of item"
:key="innerItem.id"
@click="handleCityClick(innerItem.name)"
>
{{innerItem.name}}
div>
div>
div>
// 监听letter的变化
watch: {
letter() {
if (this.letter) {
// 获取字母对应的区域的元素
// this.$refs[this.letter]返回的是数据,此处选择数组的首项
const element = this.$refs[this.letter][0]
// 滚动到对应的元素
this.scroll.scrollToElement(element)
}
}
},
mounted() {
// 创建better-scroll实例
this.scroll = new Bscroll(this.$refs.wrapper)
}
(4)上下滑动字母表组件,list组件也随之滚动,需绑定3个事件
<template>
<ul class="list">
<li
class="item"
v-for="item of letters"
:key="item"
:ref="item"
@touchstart.prevent="handleTouchStart"
@touchmove="handleTouchMove"
@touchend="handleTouchEnd"
@click="handleLetterClick"
>
{{item}}
li>
ul>
template>
handleTouchStart() {
this.touchStatus = true
},
handleTouchMove(e) {
if (this.touchStatus) {
if (this.timer) {
clearTimeout(this.timer)
}
// 通过函数节流,减少过高频次的操作,提高性能
this.timer = setTimeout(() => {
// 当前手指滑动的位置(离顶部的距离)
const touchY = e.touches[0].clientY - 79
// 当前手指滑动位置所对应的字母下标
const index = Math.floor((touchY - this.startY) / 20)
if (index >= 0 && index < this.letters.length) {
// 触发change事件,该事件在City.vue中被监听
this.$emit('change', this.letters[index])
}
}, 16)
}
},
handleTouchEnd() {
this.touchStatus = false
}
(5)通过截留函数,管理滑动事件
(1)v-model对输入的关键字进行双向绑定
(2)通过输入的关键字管理是否显示搜索结果
<template>
<div>
<div class="search">
<input v-model="keyword" class="search-input" type="text" placeholder="输入城市名或拼音" />
div>
<div
class="search-content"
v-show="keyword"
ref="search"
>
<ul>
<li
class="search-item border-bottom"
v-for="item of list"
:key="item.id"
@click="handleCityClick(item.name)"
>
{{item.name}}
li>
<li class="search-item border-bottom" v-show="hasNoData">
没有找到匹配数据
li>
ul>
div>
div>
template>
(3)监听关键字,运用截留函数,在city中查找数据,并将结果添加到list数组中。
(4)当list中无数据时,显示没有找到匹配数据
(1)安装vuex
npm install vuex –save
(2)Vuex可以看做一个仓库store,有State,Mutations组成。
1)State存储所有的公用数据,组件使用数据时,调动State即可。
2)Mutations中放置的是一个个对State同步的修改方法。
(3)在src目录下,新建store文件夹,在store文件夹下新建index.js文件。
// store文件夹下index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
export default new Vuex.Store({
state: {
city: '北京'
},
mutations: {
changeCity(state, city){
state.city = city;
}
}
})
(4)在main.js中引入store并使用。这样每个组件中都可以访问到store中管理的数据
import Vue from 'vue'
import App from './App'
import router from './router'
// 作用是引入vuex,vuex在该文件夹下被引用
import store from './store'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
components: { App },
template: ' '
})
(5)添加点击城市事件,通过路由的push方法跳转到首页
methods: {
// 将city作为参数
handleCityClick (city) {
// 直接通过commit调用mutations方法,而不使用actions方法
// this.$store.commit('changeCity', city)
// 直接使用vuex中mutation暴露出来的changeCity方法
this.changeCity(city)
// 改变城市后,跳转到首页
this.$router.push('/')
},
...mapMutations(['changeCity'])
}
(1)使用localStorage功能记录之前操作的内容
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
let defaultCity = '北京';
try {
if (localStorage.city) {
defaultCity = localStorage.city;
}
} catch (e) {
}
export default new Vuex.Store({
state: {
city: defaultCity
},
mutations: {
changeCity(state, city){
state.city = city;
try {
if (localStorage.city) {
defaultCity = localStorage.city;
}
} catch (e) {
}
}
}
})
(2)store中的内容变多,有必要对其进行拆分
1)创建state.js文件
// 建议在使用localStorage的时候,就要使用try-catch
let defaultCity = '深圳'
try {
if (localStorage.city) {
defaultCity = localStorage.city
}
} catch (e) {}
export default {
city: defaultCity
}
2)创建mutations.js文件
export default {
changeCity (state, city) {
state.city = city
// 使用localStorage记录数据
try {
localStorage.city = city
} catch (e) {}
}
}
3)在index.js中引入state.js和mutations.js文件
import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'
import mutations from './mutations'
Vue.use(Vuex)
export default new Vuex.Store({
state,
mutations
})
(3)mapState是vuex的高级用法,将vuex中的数据映射到city计算属性中
1)在home的header组件中,将vuex组件中的数据映射到该组件的计算属性中
<script>
import { mapState } from 'vuex'
export default {
name: 'HomeHeader',
// 将city放到vuex中,此处无需接收
// props: {
// city: String
// }
// 将vuex组件中的数据映射到该组件的计算属性中
computed: {
...mapState(['city'])
}
}
</script>
2)其它相关组件同理也如此。
<script>
import { mapState, mapMutations } from 'vuex'
export default {
name: 'CityList',
props: {
hot: Array,
cities: Object,
letter: String
},
computed: {
// mapState也可以放对象
...mapState({
currentCity: 'city'
})
}
</script>
(4)mapMutations是vuex的高级用法,将changeCity方法映射到各组件中
1)List组件和Search组件
<script>
import { mapState, mapMutations } from 'vuex'
export default {
name: 'CityList',
props: {
hot: Array,
cities: Object,
letter: String
},
methods: {
// 将city作为参数
handleCityClick (city) {
this.changeCity(city)
this.$router.push('/')
},
// mapMutations将changeCity方法映射到该组件中
...mapMutations(['changeCity'])
}
}
</script>
(1)每次切换页面,ajax都会发送请求,可以使用keep-alive,路由中的内容被加载之后,就放到内存中,供下次使用
<template>
<div id="app">
<keep-alive>
<router-view/>
keep-alive>
div>
template>
(2)但重新选择城市,发现因为keep-alive缓存的原因,并没有发起任何ajax请求。
解决方法:
1)使用keep-alive时,会多出个生命周期函数,当页面重新显示的时候,会执行activated函数
2)在该mounted生命周期函数内,记录当前城市
mounted () {
// 记录当前的城市
this.lastCity = this.city
// 发送ajax请求
this.getHomeInfo()
}
3)重新回到首页时,在activated生命周期函数中,判断当前城市与之前城市是否相同,如果不同,重新请求数据
activated () {
// 当城市列表中选择的城市与之前的城市不一致时,重新请求ajax数据
if (this.lastCity !== this.city) {
// 先记录城市
this.lastCity = this.city
this.getHomeInfo()
}
}