Vue实战项目开发--城市列表页面
城市选择页面路由配置
- 创建一个city-router的分支,用于城市列表的功能实现
路由配置,修改router/index.js的内容
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Home',
component: () => import( '@/pages/home/Home')
},
{
path: '/city',
name: 'City',
component: () => import( '@/pages/city/City')
}
]
})
然后在pages文件夹下新建city文件夹,并创建City.vue,最后在city文件夹下新建components文件夹,然后创建City.vue的顶部Header.vue
City.vue:
<template>
<div>
<city-header></city-header>
</div>
</template>
<script>
import CityHeader from './components/Header'
export default {
name:'City',
components: {
CityHeader
},
data () {
return {
}
}
}
</script>
<style lang="stylus" scoped>
</style>
Header.vue:
<template>
<div class="header">
城市选择
<router-link to='/'>
<div class="iconfont header-back"></div>
</router-link>
</div>
</template>
<script>
export default {
name:'CityHeader',
data () {
return {
}
}
}
</script>
<style lang="stylus" scoped>
@import '~styles/varibles.styl'
.header
position relative
height $headerHeight
line-height $headerHeight
text-align center
color #fff
background $bgColor
font-size .32rem
.header-back
position absolute
top 0
left 0
width .64rem
text-align center
font-size .4rem
color #ffffff
</style>
实现点击首页的header部分的成都进入城市列表和点击城市列表的返回按钮返回首页,这里使用router-link实现的,router-link类似a标签
<router-link to='/city'>
<div class="header-right">
{
{this.city}}
<i class="iconfont arrow-icon"></i>
</div>
</router-link>
<router-link to='/'>
<div class="iconfont header-back"></div>
</router-link>
代码优化:这里把顶部的height在varibles.styl文件中定义为一个常量,方便后期修改
$headerHeight = .86rem
使用
@import '~styles/varibles.styl' (这里需要先引入才可以使用)
height $headerHeight
line-height $headerHeight
最后,把代码提交到GitHub上
搜索框布局
- 创建city-search分支,然后把本地的项目改在新建的这个分支上进行开发
在components中新建Search.vue文件
<template>
<div class="search">
<input class="search-input" type="text" placeholder="输入城市名或拼音">
</div>
</template>
<script>
export default {
name:'CitySearch',
data () {
return {
}
}
}
</script>
<style lang="stylus" scoped>
@import '~styles/varibles.styl'
.search
height .72rem
padding 0 .1rem
background $bgColor
.search-input
box-sizing border-box
width 100%
height .62rem
padding 0 .1rem
line-height .62rem
text-align center
border-radius .06rem
color #666666
</style>
然后在City.vue中引入并使用
<template>
<div>
<city-header></city-header>
<city-search></city-search>
</div>
</template>
<script>
import CityHeader from './components/Header'
import CitySearch from './components/Search'
export default {
name:'City',
components: {
CityHeader,
CitySearch
},
data () {
return {
}
}
}
</script>
<style lang="stylus" scoped>
</style>
然后搜索框布局就完成了,最后把代码提交到GitHub上
列表布局
- 创建city-list分支,然后把本地的项目改在新建的这个分支上进行开发
在components中新建List.vue文件
<template>
<div class="list">
<div class="area">
<div class="title border-topbottom">当前城市</div>
<div class="button-list">
<div class="button-wrapper">
<div class="button">成都</div>
</div>
</div>
</div>
<div class="area">
<div class="title border-topbottom">热门城市</div>
<div class="button-list">
<div class="button-wrapper">
<div class="button">成都</div>
</div>
<div class="button-wrapper">
<div class="button">成都</div>
</div>
<div class="button-wrapper">
<div class="button">成都</div>
</div>
<div class="button-wrapper">
<div class="button">成都</div>
</div>
</div>
</div>
<div class="area">
<div class="title border-topbottom">A</div>
<div class="item-list">
<div class="item border-bottom">阿拉尔</div>
<div class="item border-bottom">阿拉尔</div>
<div class="item border-bottom">阿拉尔</div>
<div class="item border-bottom">阿拉尔</div>
<div class="item border-bottom">阿拉尔</div>
<div class="item border-bottom">阿拉尔</div>
</div>
</div>
<div class="area">
<div class="title border-topbottom">B</div>
<div class="item-list">
<div class="item border-bottom">阿拉尔</div>
<div class="item border-bottom">阿拉尔</div>
<div class="item border-bottom">阿拉尔</div>
<div class="item border-bottom">阿拉尔</div>
<div class="item border-bottom">阿拉尔</div>
<div class="item border-bottom">阿拉尔</div>
</div>
</div>
<div class="area">
<div class="title border-topbottom">C</div>
<div class="item-list">
<div class="item border-bottom">阿拉尔</div>
<div class="item border-bottom">阿拉尔</div>
<div class="item border-bottom">阿拉尔</div>
<div class="item border-bottom">阿拉尔</div>
<div class="item border-bottom">阿拉尔</div>
<div class="item border-bottom">阿拉尔</div>
</div>
</div>
<div class="area">
<div class="title border-topbottom">D</div>
<div class="item-list">
<div class="item border-bottom">阿拉尔</div>
<div class="item border-bottom">阿拉尔</div>
<div class="item border-bottom">阿拉尔</div>
<div class="item border-bottom">阿拉尔</div>
<div class="item border-bottom">阿拉尔</div>
<div class="item border-bottom">阿拉尔</div>
</div>
</div>
</div>
</template>
<script>
export default {
name:'CityList',
}
</script>
<style lang="stylus" scoped>
@import '~styles/varibles.styl'
.border-topbottom
&:before
border-color #ccc
&:after
border-color #ccc
.border-bottom
&:before
border-color #ccc
.list
overflow hidden
position absolute
top 1.58rem
left 0
right 0
bottom 0
.title
line-height .44rem
background #eee
padding-left .2rem
color #666
font-size .26rem
.button-list
padding .1rem .6rem .1rem .1rem
overflow hidden
.button-wrapper
float left
width 33.33%
.button
margin .1rem
padding .1rem 0
text-align center
border .02rem solid #ccc
border-radius .06rem
.item-list
.item
line-height .76rem
padding-left .2rem
</style>
然后在City.vue中引入并使用
<template>
<div>
<city-header></city-header>
<city-search></city-search>
<city-list></city-list>
</div>
</template>
<script>
import CityHeader from './components/Header'
import CitySearch from './components/Search'
import CityList from './components/List'
export default {
name:'City',
components: {
CityHeader,
CitySearch,
CityList
}
}
</script>
<style lang="stylus" scoped>
</style>
然后列表布局就完成了,最后把代码提交到GitHub上
Better-scroll的使用及字母表布局
安装better-scroll插件
npm install better-scroll --save
在文档可以看到better-scroll的使用better-scroll的GitHub地址
修改List.vue的代码
(使用ref来获取当前元素)
<- 这里放之前的内容 ->
引入并使用
import BScroll from ‘better-scroll’
export default {name:'CityList',
mounted () {
this.scroll = new BScroll(this.$refs.wrapper)
}
}
完整代码
当前城市
热门城市
A
阿拉尔
阿拉尔
阿拉尔
阿拉尔
阿拉尔
阿拉尔
B
阿拉尔
阿拉尔
阿拉尔
阿拉尔
阿拉尔
阿拉尔
C
阿拉尔
阿拉尔
阿拉尔
阿拉尔
阿拉尔
阿拉尔
D
阿拉尔
阿拉尔
阿拉尔
阿拉尔
阿拉尔
阿拉尔
better-scroll就引入并使用完成了
字母表布局:在componens文件夹下新建Alphabet.vue
<template>
<ul class="list">
<li class="item">A</li>
<li class="item">A</li>
<li class="item">A</li>
<li class="item">A</li>
<li class="item">A</li>
</ul>
</template>
<script>
export default {
name:'CityAlphabet',
}
</script>
<style lang="stylus" scoped>
@import '~styles/varibles.styl'
.list
display flex
flex-direction column
justify-content center
position absolute
top 1.58rem
right 0
bottom 0
width .4rem
.item
line-height .4rem
text-align center
color $bgColor
</style>
然后在City.vue中引入:
<template>
<div>
<city-header></city-header>
<city-search></city-search>
<city-list></city-list>
<city-alphabet></city-alphabet>
</div>
</template>
<script>
import CityHeader from './components/Header'
import CitySearch from './components/Search'
import CityList from './components/List'
import CityAlphabet from './components/Alphabet'
export default {
name:'City',
components: {
CityHeader,
CitySearch,
CityList,
CityAlphabet
},
data () {
return {
}
}
}
</script>
<style lang="stylus" scoped>
</style>
字母表布局就完成了,最后把代码提交到线上
页面的动态数据渲染
- 创建city-Ajax分支,然后把本地的项目改在新建的这个分支上进行开发
- 使用Ajax模拟网络请求,在static/mock下新建一个city.json(有需要的可以在我的GitHub上拉取),在使用axios来模拟请求数据
City.vue的js部分:
<script>
import axios from 'axios'
import CityHeader from './components/Header'
import CitySearch from './components/Search'
import CityList from './components/List'
import CityAlphabet from './components/Alphabet'
export default {
name:'City',
components: {
CityHeader,
CitySearch,
CityList,
CityAlphabet
},
data () {
return {
cities: {},
hotCities: []
}
},
methods: {
getCityInfo () {
axios.get('/api/city.json')
.then(this.handleGetCityInfoSucc)
},
handleGetCityInfoSucc (res) {
console.log(res)
res = res.data
if(res.ret && res.data){
const data = res.data
this.cities = data.cities
this.hotCities = data.hotCities
}
}
},
mounted () {
this.getCityInfo()
}
}
</script>
City.vue:
<template>
<div>
<city-header></city-header>
<city-search></city-search>
<city-list :cities="cities" :hot="hotCities"></city-list>
<city-alphabet :cities="cities"></city-alphabet>
</div>
</template>
<script>
import axios from 'axios'
import CityHeader from './components/Header'
import CitySearch from './components/Search'
import CityList from './components/List'
import CityAlphabet from './components/Alphabet'
export default {
name:'City',
components: {
CityHeader,
CitySearch,
CityList,
CityAlphabet
},
data () {
return {
cities: {},
hotCities: []
}
},
methods: {
getCityInfo () {
axios.get('/api/city.json')
.then(this.handleGetCityInfoSucc)
},
handleGetCityInfoSucc (res) {
console.log(res)
res = res.data
if(res.ret && res.data){
const data = res.data
this.cities = data.cities
this.hotCities = data.hotCities
}
}
},
mounted () {
this.getCityInfo()
}
}
</script>
<style lang="stylus" scoped>
</style>
List.vue:
<template>
<div class="list" ref='wrapper'>
<div>
<div class="area">
<div class="title border-topbottom">当前城市</div>
<div class="button-list">
<div class="button-wrapper">
<div class="button">成都</div>
</div>
</div>
</div>
<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">
<div class="button">{
{item.name}}</div>
</div>
</div>
</div>
<div class="area" v-for="(item,key) of cities" :key="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">{
{innerItem.name}}</div>
</div>
</div>
</div>
</div>
</template>
<script>
import BScroll from 'better-scroll'
export default {
name:'CityList',
props: {
hot: Array,
cities: Object
},
mounted () {
this.scroll = new BScroll(this.$refs.wrapper)
}
}
</script>
<style lang="stylus" scoped>
@import '~styles/varibles.styl'
.border-topbottom
&:before
border-color #ccc
&:after
border-color #ccc
.border-bottom
&:before
border-color #ccc
.list
overflow hidden
position absolute
top 1.58rem
left 0
right 0
bottom 0
.title
line-height .54rem
background #eee
padding-left .2rem
color #666
font-size .26rem
.button-list
padding .1rem .6rem .1rem .1rem
overflow hidden
.button-wrapper
float left
width 33.33%
.button
margin .1rem
padding .1rem 0
text-align center
border .02rem solid #ccc
border-radius .06rem
.item-list
.item
line-height .76rem
padding-left .2rem
</style>
Alphabet.vue:
<template>
<ul class="list">
<li class="item" v-for="(item,key) of cities" :key="key">{
{key}}</li>
</ul>
</template>
<script>
export default {
name:'CityAlphabet',
props: {
cities: Object
}
}
</script>
<style lang="stylus" scoped>
@import '~styles/varibles.styl'
.list
display flex
flex-direction column
justify-content center
position absolute
top 1.58rem
right 0
bottom 0
width .4rem
.item
line-height .4rem
text-align center
color $bgColor
</style>
最后看一下最后的效果,记住把代码提交到GitHub上
![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dvXzkyMTExMA_size_16_color_FFFFFF_t_70][]
兄弟组建间联动
实现点击右侧字母,左边的内容跟着字母变化
![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dvXzkyMTExMA_size_16_color_FFFFFF_t_70 1][]
就是点击H字母,城市列表就跳到H首字母的城市列表
这里是通过在Alphabet.vue监听点击字母的点击事件,然后把点击对应的元素的值传给父组件,父组件再将这个值传给List.vue子组件的
Alphabet.vue(增加了handleLetterClick,通过emit把值传给父组件的):
<li class="item" v-for="(item,key) of cities" :key="key" @click="handleLetterClick" > {
{key}}
</li>
City.vue(通过@change=”handleLetterChange”来接受Alphabet.vue子组件传过来的值,并在data中定义letter值,在前面的方法把从子组件得到的值赋给data中定义好的letter,然后把这个值通过属性传给List.vue子组件):
<city-header></city-header>
<city-search></city-search>
<city-list
:cities="cities"
:hot="hotCities"
:letter="letter"
></city-list>
<city-alphabet
:cities="cities"
@change="handleLetterChange"
></city-alphabet>
List.vue(在这里使用props接受父组件传来的letter值,并使用watch来监听letter值的变化,然后让内容跟随右侧字母的变化):
<template>
<div class="list" ref='wrapper'>
<div>
<div class="area">
<div class="title border-topbottom">当前城市</div>
<div class="button-list">
<div class="button-wrapper">
<div class="button">成都</div>
</div>
</div>
</div>
<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">
<div class="button">{
{item.name}}</div>
</div>
</div>
</div>
<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">{
{innerItem.name}}</div>
</div>
</div>
</div>
</div>
</template>
<script>
import BScroll from 'better-scroll'
export default {
name:'CityList',
props: {
hot: Array,
cities: Object,
letter: String
},
mounted () {
this.scroll = new BScroll(this.$refs.wrapper)
},
watch: {
letter () {
if (this.letter) {
const element = this.$refs[this.letter][0]
this.scroll.scrollToElement(element)
}
}
}
}
</script>
<style lang="stylus" scoped>
@import '~styles/varibles.styl'
.border-topbottom
&:before
border-color #ccc
&:after
border-color #ccc
.border-bottom
&:before
border-color #ccc
.list
overflow hidden
position absolute
top 1.58rem
left 0
right 0
bottom 0
.title
line-height .54rem
background #eee
padding-left .2rem
color #666
font-size .26rem
.button-list
padding .1rem .6rem .1rem .1rem
overflow hidden
.button-wrapper
float left
width 33.33%
.button
margin .1rem
padding .1rem 0
text-align center
border .02rem solid #ccc
border-radius .06rem
.item-list
.item
line-height .76rem
padding-left .2rem
</style>
在左侧字母表中做上下拖拽的时候,城市列表内容也跟着变化
主要是监听拖拽时间,在li增加了三个事件,分别是touchstart、touchmove、touchend来监听拖拽事件,同时在li上使用ref,在data定义了一个touchStatus来保存是否在拖拽的状态,并使用计算属性把cities中的字母取出来放在数组letters中,最后计算拖拽到那个字母:
handleTouchStart () {
this.touchStatus = true
},
handleTouchMove (e) {if(this.touchStatus){
const startY = this.$refs['A'][0].offsetTop
const touchY = e.touches[0].clientY -79
const index = Math.floor((touchY-startY) / 20)
if(index >= 0 && index < this.letters.length) {
this.$emit('change',this.letters[index])
}
}
},
handleTouchEnd () {this.touchStatus = false
}
Alphabet.vue:
<liclass="item" v-for="item of letters" :key="item" :ref="item" @touchstart="handleTouchStart" @touchmove="handleTouchMove" @touchend="handleTouchEnd" @click="handleLetterClick"
>
{
{item}}
列表切换性能优化
把A字母离顶部的高度使用updated钩子函数来计算,不用每次执行handleTouchMove函数的时候都计算
![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dvXzkyMTExMA_size_16_color_FFFFFF_t_70 2][]
函数节流(定义了一个timer,如果正在执行拖拽操作,让它延迟16毫秒来执行,如果在16毫秒之间又执行了手指滑动操作,它就会把上次要做的操作清楚掉,重新执行这次要做的事情,从而减少handleTouchMove函数的执行频率)
![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dvXzkyMTExMA_size_16_color_FFFFFF_t_70 3][]
Alphabet.vue:
<template>
<ul class="list">
<li
class="item"
v-for="item of letters"
:key="item"
:ref="item"
@touchstart="handleTouchStart"
@touchmove="handleTouchMove"
@touchend="handleTouchEnd"
@click="handleLetterClick"
>
{
{item}}
</li>
</ul>
</template>
<script>
export default {
name:'CityAlphabet',
props: {
cities: Object
},
data () {
return {
touchStatus: false,
startY: 0,
timer: ''
}
},
computed: {
letters () {
const letters = []
for( let i in this.cities) {
letters.push(i)
}
return letters
}
},
updated () {
this.startY = this.$refs['A'][0].offsetTop
},
methods: {
handleLetterClick (e) {
this.$emit('change',e.target.innerText)
},
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) {
this.$emit('change',this.letters[index])
}
}, 16)
}
},
handleTouchEnd () {
this.touchStatus = false
}
}
}
</script>
<style lang="stylus" scoped>
@import '~styles/varibles.styl'
.list
display flex
flex-direction column
justify-content center
position absolute
top 1.58rem
right 0
bottom 0
width .4rem
.item
line-height .4rem
text-align center
color $bgColor
</style>
最后git提交代码到GitHub上
搜索功能实现
- City.vue传值到Search.vue子组件,通过watch监听用户输入,从而修改输入框下面显示的数组
- 使用v-show判断用户是否输入了数据,从而实现查找到的数组内容区域的显示隐藏
- 使用v-show判断用户输入的值是否有对应的值,来实现查找到的数组数据的显示和没有查到内容时的提示
还使用了函数节流,让用户在输入后100毫秒再执行,如果用户在100毫秒之间再输入就会把上次的操作清楚掉,增强性能
Search.vue:
{
{item.name}}
没有找到匹配数据
使用Vuex实现数据共享
- 首先创建一个city-vuex的分支,这里需要实现的功能是点击城市列表页面中城市,首页的城市会发生改变
vuex数据框架是vue大型项目的数据传输使用的框架
重要: vuex,我们可以想象成一个仓库,其中有State、Actions、Mutations;State是所有的公用数据都存在State中,组建需要使用公用的数据,直接去调用State就可以了;Actions是我们可以把需要实现异步操作或者是一些比较复杂的同步操作(批量的同步操作)存在Actions中;Mutations是对State同步的修改操作;组件中怎么使用?(1.组件先去调用Actions,紧接着Actions去调用Mutations,然后Motations去修改State中的数据;2.组件直接调用Motations去修改State中的数据)
![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dvXzkyMTExMA_size_16_color_FFFFFF_t_70 5][]
在项目中使用,先安装这个vuex
npm install vuex --save
然后在项目中创建这个”仓库区域”,我们把它叫做store,并创建index.js文件,最后在main.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
}
}
})
在组件中的使用(在List.vue和Search.vue中使用到了,实现效果就是在点击城市列表页的时候,通过调用mutations中的方法来改变State中的数据),对对应的地方绑定handleCityClick()方法并且在methods中定义这个方法
<div
class="button-wrapper"
v-for="item of hot"
:key="item.id"
@click="handleCityClick(item.name)"
>
<div class="button">{
{item.name}}</div>
</div>
<div
class="item border-bottom"
v-for="innerItem of item"
:key="innerItem.id"
@click="handleCityClick(innerItem.name)"
>
{
{innerItem.name}}
</div>
handleCityClick (city) {
this.$store.commit('changeCity', city)
this.$router.push('/')
}
这个调用mutations中的方法使用的是commit,可以从上面的图看到;这样最简单的使用vuex就完成了
Vuex的高级使用及localStorage
上面实现的功能会有问题,就是每次重启应用的时候,选择的城市会重置为State中预设的城市名,为了解决这个问题,在这里就引入了h5中的localStorage本地缓存来实现
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
city: localStorage.city || '成都'
},
mutations: {
changeCity (state, city) {
state.city = city
localStorage.city = city
}
}
})
改进版(防止用户没有开启本地缓存功能或者是隐身模式):
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 {
localStorage.city = city
} catch (e) {}
}
}
})
最终版(拆分成了state.js和mutations.js):
// state.js
let defaultCity = '上海'
try {
if (localStorage.city) {
defaultCity = localStorage.city
}
} catch (e) {}
export default {
city: defaultCity
}
// mutations.js
export default{
changeCity (state, city) {
state.city = city
try {
localStorage.city = city
} catch (e) {}
}
}
// index.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
})
还有个样式问题,就是选择四个字的城市时,顶部会被撑开,直接修改home/components下的Header.vue中的.header-right样式
.header-right
min-width 1.04rem //修改的
padding 0 .1rem //新增的
float right
text-align center
color #ffffff
优化代码:使用vuex提供的mapState来简化代码
// home下的Header.vue的js部分
<script>
import { mapState } from 'vuex' //新增
export default {
name: 'HomeHeader',
computed: { //计算属性新增的
...mapState(['city'])
}
}
</script>
使用:
<router-link to='/city'>
<div class="header-right">
{
{this.city}} //从this.$store.state.city精简成的this.city
<i class="iconfont arrow-icon"></i>
</div>
</router-link>
city/components中List.vue:
<template>
<div class="list" ref='wrapper'>
<div>
<div class="area">
<div class="title border-topbottom">当前城市</div>
<div class="button-list">
<div class="button-wrapper">
<div class="button">{
{this.currentCity}}</div>
</div>
</div>
</div>
<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>
<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>
</div>
</div>
</template>
<script>
import BScroll from 'better-scroll'
import { mapState, mapMutations } from 'vuex'
export default {
name:'CityList',
computed: {
...mapState({
currentCity: 'city' //这里使用的对象,就是把state中的city映射到这个组件的计算属性中这个值
})
},
props: {
hot: Array,
cities: Object,
letter: String
},
methods: {
handleCityClick (city) {
// this.$store.commit('changeCity', city) 使用mutations的方法之前的调用
this.changeCity(city)
this.$router.push('/')
},
...mapMutations(['changeCity']) //使用mapMutations来简化mutations中的方法调用
},
mounted () {
this.scroll = new BScroll(this.$refs.wrapper)
},
watch: {
letter () {
if (this.letter) {
const element = this.$refs[this.letter][0]
this.scroll.scrollToElement(element)
}
}
}
}
</script>
<style lang="stylus" scoped>
@import '~styles/varibles.styl'
.border-topbottom
&:before
border-color #ccc
&:after
border-color #ccc
.border-bottom
&:before
border-color #ccc
.list
overflow hidden
position absolute
top 1.58rem
left 0
right 0
bottom 0
.title
line-height .54rem
background #eee
padding-left .2rem
color #666
font-size .26rem
.button-list
padding .1rem .6rem .1rem .1rem
overflow hidden
.button-wrapper
float left
width 33.33%
.button
margin .1rem
padding .1rem 0
text-align center
border .02rem solid #ccc
border-radius .06rem
.item-list
.item
line-height .76rem
padding-left .2rem
</style>
其他的组件一样使用这种方法来精简代码(这里我把之前创建的没有用的文件夹List和组件也删除了)
另外讲一下vuex中的getter和module,这 getter就是store 的计算属性,通过把State中的数据计算后,组件就可以之间调用getter使用;module是当由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。 为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割
![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dvXzkyMTExMA_size_16_color_FFFFFF_t_70 6][]
使用keep-alive优化页面性能
- 首先创建在GitHub一个city-keepalive的分支
在App.vue中使用
标签 <template>
<div id="app">
<keep-alive>
<router-view/>
<!-- 显示的是当前路由地址所对应的内容 -->
</keep-alive>
</div>
</template>
<script>
export default {
name: 'App'
}
</script>
<style></style>
作用是路由的内容加载过一次之后,就把路由中的内容放到内存之中,下次再重新进这个路由时,不需要重新渲染组件
还有逻辑问题,就是在重新选择城市后在Home.vue中需要重新进行Ajax请求,在Home.vue中引入vuex
import { mapState } from 'vuex'
修改getHomeinfo方法,在请求的时候带上state中的city
getHomeinfo () {
axios.get('/api/index.json?city=' + this.city)
.then(this.getHomeinfoSucc)
},
这样修改后,重新选择城市也不会重新进行Ajax请求,这里就需要用到activated(使用keep-alive才有的钩子函数)使用mounted和activated这两个钩子函数来实现
mounted () {
this.lastCity = this.city
this.getHomeinfo()
},
activated () {
if (this.lastCity !== this.city) {
this.lastCity = this.city
this.getHomeinfo()
}
}
最后把代表提交到GitHub上
还没有评论,来说两句吧...