第10章 Vue項(xiàng)目開(kāi)發(fā)之城市

10-1.city頁(yè)面路由配置

1.添加路由配置

// router/index.js文件
import City from '@/pages/city/City'
export default new Router({
  routes: [
    {
      path: '/',
      name: 'Home',
      component: Home
    },
    {
      path: '/city',
      name: 'City',
      component: City
    }
  ]
})

2.添加相對(duì)應(yīng)的頁(yè)面
在pages文件下面添加city文件夾和City.vue文件


效果圖

3.初始化City.vue頁(yè)面

<template>
  <div>
    city
  </div>
</template>

<script>
export default {
  name: 'City'
}
</script>
<style lang="stylus" scoped>

</style>
10-2.city-header部分制作
//city/City.vue
<template>
  <div>
    <city-header></city-header>
  </div>
</template>

<script>
import CityHeader from './components/Header'
export default {
  name: 'City',
  components: {
    CityHeader
  }
}
</script>
<style lang="stylus" scoped>

</style>


//city/components/header.vue
<template>
  <div class="header">
    城市選擇
    <router-link to="/">
      <div class="iconfont back-city">&#xe696;</div>
    </router-link>
  </div>
</template>

<script>
export default {
  name: 'CityHeader'
}
</script>
<style lang="stylus" scoped>
@import '~styles/varibles.styl'
.header
  position relative
  height $headHeight
  line-height $headHeight
  background $bgColor
  text-align center
  color #ffffff
  .back-city
    position absolute
    left 0
    top 0
    width .64rem
    text-align center
    padding 0 .1rem
    font-size .48rem
    color #fff
</style>

效果圖
10-3.city-search部分制作

上面咱們已經(jīng)完成了頭部的制作,這一節(jié)咱們來(lái)city-search的ui部分制作,隨后等咱們把city列表制作完成后,咱們?cè)賮?lái)制作city-search相關(guān)的邏輯部分,代碼如下

//city/components/Search.vue
<template>
  <div class="search">
    <input class="search-input" type="text" placeholder="輸入城市名稱或者拼音" />
  </div>
</template>

<script>
export default {
  name: 'CitySearch'
}
</script>
<style lang="stylus" scoped>
@import '~styles/varibles.styl'
  .search
    height .722rem
    padding 0 .1rem
    background $bgColor
    .search-input
      box-sizing border-box
      width 100%
      height .62rem
      padding 0 .1rem
      line-height .62rem
      border-radius .06rem
      color #666
      text-align center
</style>

city/City.vue,在city的主頁(yè)面引入我們制作好的city-search模塊


image.png
10-4 city-list、city-ajax 、city-vuex、city-search-logic 部分的制作
城市整體效果圖

Ui上面的制作,直接從github下載下來(lái),git checkout 到不同的分支,就能看到代碼了,總結(jié)嘛,不能能把所有的項(xiàng)目中的代碼都展示出來(lái),更多的是展示難點(diǎn)、思路、注意事項(xiàng)等等一些小細(xì)節(jié)地方。
city-ajax部分和index-ajax 方式是一樣,在這里咱們就不再次贅述了

知識(shí)點(diǎn)1:BetterScroll 的使用,讓城市列表可以滾動(dòng)起來(lái)

//安裝better-scroll
npm install better-scroll -S

在這個(gè)使用better-scroll的時(shí)候我們需要注意三點(diǎn)

  • dom結(jié)構(gòu)(要符合這種結(jié)構(gòu))
<div class="wrapper">
  <ul class="content">
    <li>...</li>
    <li>...</li>
    ...
  </ul>
  <!-- you can put some other DOMs here, it won't affect the scrolling
</div>
  • 樣式(要滾動(dòng)的list 要脫離文檔流)
.list 
  overflow: hidden;
  position: absolute;
  top: 1.6rem;
  left: 0;
  right: 0;
  bottom: 0;
  • 在vue中的調(diào)用和使用方法
//dom部分
<div class="list" ref="wrapper">
.....
</div>

//js部分
import BScroll from 'better-scroll'
mounted () {
    this.scroll = new BScroll(this.$refs.wrapper)
}

知識(shí)點(diǎn)2兄弟組件數(shù)據(jù)傳遞
我們知道:
City.vue是父組件
components/List.vue是一個(gè)子組件
components/Alphabet.vue也是一個(gè)子組件

那么子組件(Alphabet.vue)如何和子組件(List.vue)進(jìn)行通信呢?
現(xiàn)在有這樣的一個(gè)需求,就是當(dāng)我們點(diǎn)擊右側(cè)的字母(代碼在Alphabet.vue中),列表(List.vue)能自動(dòng)滾動(dòng)相對(duì)應(yīng)的列表字母模塊部分,那么這個(gè)過(guò)程就是一個(gè)子組件和子組件的通信(兄弟組件數(shù)據(jù)傳遞)

思路:
第一步:子組件(Alphabet.vue)點(diǎn)擊字母的時(shí)候,通過(guò)$emit發(fā)送一個(gè)'change'的方法,并且把攜帶的點(diǎn)擊入?yún)鬟f給父組(City.vue)

//dom部分
<li
      class="item"
      v-for="item of letters"
      :key="item"
      @click="handleLetterClick" //觸發(fā)點(diǎn)擊事件
    >{{item}}</li>

//js部分
methods: {
    handleLetterClick (e) {
      this.$emit('change', e.target.innerText)
    }
}

第二步:父組件(City.vue)通過(guò)屬性來(lái)監(jiān)聽(tīng)‘change’事件,同時(shí)創(chuàng)建一個(gè)新的方法,在此方法中來(lái)接受子組件傳遞過(guò)來(lái)的參數(shù),隨后把入?yún)⒎湃氲絛ata初始化的letter中,再然后,把letter獲得入?yún)⒁詫傩缘姆绞絺鬟f給city-list組件

//1)dom 來(lái)監(jiān)聽(tīng)子組件發(fā)出來(lái)的change
<city-alphabet :cities="cities" @change="handleLetterClick"></city-alphabet>

//4)dom 父組件從子組件那拿來(lái)的數(shù)據(jù)(letter)傳遞給新的子組件
<city-list :cities="cities" :hotCities="hotCities" :letter="letter"></city-list>

//2)初始化data中的letter值 用來(lái)存儲(chǔ)子組件出來(lái)的入?yún)?data () {
    return {
      letter: ''
    }
},

//3)js change 創(chuàng)建的方法 來(lái)接受子組件傳遞過(guò)來(lái)的值,并把它存儲(chǔ)到data里面
handleLetterClick (letter) {
      this.letter = letter
}

第三步:子組件(List.vue)通過(guò)屬性props來(lái)接受父組件傳過(guò)來(lái)的值

//js
props: {
    letter: String//接受父組件傳遞過(guò)來(lái)的值
},

//js 監(jiān)聽(tīng)傳過(guò)來(lái)值的變化
watch: {
    letter () {
      if (this.letter) {
        const element = this.$refs[this.letter][0]  //通過(guò)獲取字母的值
        this.scroll.scrollToElement(element) //滾動(dòng)到指定元素模塊
      }
    }
  }

//dom 需要在字母模塊添加ref屬性
<div
  class="area"
  v-for="(item,key) of cities"
  :key="key" 
  :ref="key"http://這個(gè)key值剛好和兄弟組件傳過(guò)來(lái)的值相同
>
  <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>

知識(shí)點(diǎn)3 完成一個(gè)手指滑動(dòng)右側(cè)字母,左側(cè)區(qū)域跟著滾動(dòng)
這部分咱們需要給右側(cè)的字母綁定上三個(gè)事件:

@touchstart="handleTouchStart"
@touchmove="handleTouchMove"
@touchend="handleTouchEnd"

為了只讓在touchmove里面去觸發(fā)這些操作,所以我們需要定義個(gè)開(kāi)關(guān)(標(biāo)示位),我們把這個(gè)標(biāo)示位放在了data里面

touchStatus: false //設(shè)置為false

所以當(dāng)我們開(kāi)始滑動(dòng)的時(shí)候我們把touchStatus設(shè)置為true

handleTouchStart () {
  this.touchStatus = true
}

當(dāng)我們手指劃出觸發(fā)操作區(qū)域的時(shí)候,我們需要把標(biāo)示為設(shè)置為false

handleTouchEnd () {
  this.touchStatus = false
}

所以只有當(dāng)標(biāo)示位為true的這種情況,我們采取進(jìn)滑動(dòng)字母相對(duì)應(yīng)的操作

handleTouchMove () {
  if (this.touchStatus) {
  //滑動(dòng)過(guò)程中所對(duì)應(yīng)的邏輯
  }
}

思路
在滑動(dòng)這個(gè)過(guò)程中,最終我們?cè)谶@個(gè)頁(yè)面上下滑動(dòng)的時(shí)候,我們需要知道你滑動(dòng)的位置是第幾個(gè)字母
1、我們需要知道A字母距離頂部的距離
2、我們需要知道手指滑動(dòng)到當(dāng)前字母距離頂部的的距離
3、把上面兩個(gè)做一個(gè)差值,那么我們就可以得到當(dāng)前位置距離A字母之間的高度
4、我們把得到這個(gè)差值高度除以每個(gè)字母的高度,那么我們就得到了是第幾個(gè)字母了
根據(jù)上面這個(gè)思路,我們需要得到這個(gè)字母的數(shù)組:

computed: {
    letters () {
       const letters = []
       for (let i in this.cities) {
          letters.push(i)
       }
       return letters
    }
}

通過(guò)計(jì)算屬性,我們就可以把dom上的數(shù)據(jù)獲取從父組件傳遞過(guò)來(lái)的cities改為letters

<li
  class="item"
  v-for="item of letters" //通過(guò)計(jì)算屬性來(lái)獲得字母值
  :key="item"
  :ref="item"
  @click="handleLetterClick"
  @touchstart="handleTouchStart"
  @touchmove="handleTouchMove"
  @touchend="handleTouchEnd"
>
{{item}}
</li>

根據(jù)上面的思路咱們開(kāi)始來(lái)編寫相對(duì)應(yīng)邏輯

handleTouchMove (e) {
    //標(biāo)示位開(kāi)始
    if (this.touchStart) {
      const startY = this.$refs['A'].offsetTop //獲取字母A距離頂部的距離
      const touchY = e.touches[0].clientY - 79 //獲取手機(jī)滑動(dòng)當(dāng)前字母距離頂部距離(79是header和搜索框的高度)
      const index = Math.floor((touchY-startY) / 20) //獲得是第幾個(gè)字母

     if (index >= 0 && index < this.letters.length) {
        this.$emit('change', this.letters[index]) //在有效的索引里面去 查找是第幾個(gè)字母
     }
    }
}

其實(shí)寫到這塊我們的功能是完成了的,但是細(xì)想還有一些地方需要優(yōu)化?
初始化

data () {
  return {
    startY: 0,
    timer: null
  }
},

優(yōu)化一:每次都去求獲取字母A距離頂部的距離?

updated () {
    this.startY = this.$refs['A'][0].offsetTop
},

優(yōu)化二:滑動(dòng)字母的時(shí)候,需要做一下事件節(jié)流(通過(guò)一個(gè)定時(shí)器timer)

handleTouchMove (e) {
  if (this.touchStatus) {
    if (this.timer) {
      clearTimeout(this.timer)
    }
    this.timer = setTimeout(() => {
      const startY = this.startY
      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])
      }
    }, 16)
  }
},

知識(shí)點(diǎn)4 實(shí)現(xiàn)一個(gè)城市搜索功能
需求
1.根據(jù)字母或者漢字可以進(jìn)行檢索想要的內(nèi)容
2.當(dāng)搜索框沒(méi)數(shù)據(jù)的時(shí)候,不顯示搜索區(qū)域內(nèi)容
3.當(dāng)搜索框有數(shù)據(jù)且數(shù)據(jù)不在搜索內(nèi)容時(shí),顯示暫無(wú)搜索內(nèi)容
4.當(dāng)搜索出來(lái)的內(nèi)容比較多的時(shí)候,搜索內(nèi)容可以進(jìn)行滾動(dòng)(better-scroll)

第一步:獲取從父組件傳遞過(guò)來(lái)的cities值

props: {
    cities: Object
},

第二步:data里面初始化keyword、list、timer

data () {
  return {
    keyword: '',
    list: [],
    timer: null
  }
},

第三步:watch方法監(jiān)聽(tīng)keyword的更改、其中這里面包含timer優(yōu)化、list數(shù)據(jù)獲取、檢索操作的邏輯

watch: {
  keyword () {
    if (this.timer) {
      clearTimeout(this.timer)
    }
    if (!this.keyword) {
      this.list = []
      return false
    }
    this.timer = setTimeout(() => {
      const result = []
      for (let i in this.cities) {
        this.cities[i].forEach(value => {
          if (value.spell.indexOf(this.keyword) > -1 || value.name.indexOf(this.keyword) > -1) {
            result.push(value)
          }
        })
      }
      this.list = result
    }, 100)
  }
},

第四步:數(shù)據(jù)處理好了,要鋪到Ui上面
為了可以滾動(dòng):一定要符合better-scroll的dom結(jié)構(gòu);search-content樣式要脫離文檔流。
只有當(dāng)有關(guān)鍵字才會(huì)顯示搜索內(nèi)容;
當(dāng)關(guān)鍵字搜索沒(méi)有數(shù)據(jù)的時(shí)候,顯示”沒(méi)有搜索到匹配內(nèi)容“

<div class="search-content" ref="search" v-show="keyword">
  <ul>
    <li class="search-item border-bottom" v-for="item of list" :key="item.id">{{item.name}}</li>
    <li class="search-item border-bottom" v-show="hasNoData">沒(méi)有搜索到匹配內(nèi)容</li>
  </ul>
</div>

第五步:搜索數(shù)據(jù)有了,但是過(guò)多的時(shí)候也要可以滾動(dòng),better-scroll

mounted () {
  this.scroll = new Bscroll(this.$refs.search)
}

知識(shí)點(diǎn)5 vuex實(shí)現(xiàn)數(shù)據(jù)共享
如果學(xué)過(guò)react的同學(xué)肯定知道redux,react是處理ui層的,那么數(shù)據(jù)層就是通過(guò)redux來(lái)完成,方便我們不同頁(yè)面之間的傳值,一直值的更改等等
同樣在vue中,vue也只負(fù)責(zé)ui部分,vuex則是用來(lái)處理數(shù)據(jù)層的

vuex的原理圖

1.安裝vuex

npm install vuex -S

2.使用和調(diào)用vuex
因?yàn)関uex是處理數(shù)據(jù)模塊的,所以我們?cè)趕rc目錄下創(chuàng)建一個(gè)store目錄,在store目錄下面創(chuàng)建一個(gè)
index.js

import Vue from 'vue'
import Vuex from 'vuex'

export default new Vuex.Store({
  state: {
    city: '北京'
  }
})

創(chuàng)建好之后,我們?cè)趍ain.js文件中去調(diào)用這個(gè)文件

import store from './store'

new Vue({
  el: '#app',
  store,//根實(shí)例引入store
  router,
  components: { App },
  template: '<App/>'
})

3.應(yīng)用
在咱們這個(gè)項(xiàng)目中,首頁(yè)右上角的城市名稱是通過(guò)后端返給我們,那么我們可以通過(guò)vuex來(lái)初始化一個(gè)城市,也可以通過(guò)vuex來(lái)更改城市這個(gè)值。
在store/index.js 其實(shí)我們已經(jīng)做了city的初始化的值:北京
那么在首頁(yè)和城市頁(yè)面我們?nèi)绾潍@取vuex當(dāng)中這個(gè)值呢?

//pages/home/components/Header.vue
{{this.$store.state.city}}

//pages/city/components/List.vue  當(dāng)前城市
{{this.$store.state.city}}

點(diǎn)擊熱門城市或者點(diǎn)擊城市搜索出來(lái)列表切換城市的顯示,那么我們?nèi)ト绾胃膕tate這個(gè)值呢?

//點(diǎn)擊熱門城市事件
@click="handleCityClick(item.name)"

methods: {
  handleCityClick (city) {
      //要調(diào)用store里面的dispatch方法
      this.$store.dispatch('changeCity', city)
  }
}

上面我們已經(jīng)觸發(fā)了一個(gè)dispatch的方法,那么我們通過(guò)actions來(lái)接受這個(gè)方法
store/index.js

export default new Vuex.Store({
  state: {
    city: '上海'
  },
 actions: {
    changeCity(ctx, city) {
      //console.log(city)
      //那么action如何調(diào)用mutations呢?通過(guò)commit方法
      ctx.commit('changeCity',city)
    }
  },
  mutations: {
    changeCity (state, city) {
      state.city = city
    }
  }
})

從上面可以看出在我們發(fā)送dispatch的時(shí)候,并沒(méi)有觸發(fā)異步請(qǐng)求,或者批量的數(shù)據(jù)操作,所以上面操作,我們可以直接跳過(guò)actions這部分,不需要去觸發(fā)dispatch操作,而是直接調(diào)用commit對(duì)mutations的操作
所以上面的代碼就可以改為:

//點(diǎn)擊熱門城市事件
@click="handleCityClick(item.name)"

methods: {
  handleCityClick (city) {
      //要調(diào)用store里面的dispatch方法
      this.$store.commit('changeCity', city)  //將dispatch 改為commit
  }
}


//store/index.js
export default new Vuex.Store({
  state: {
    city: '上海'
  },
  //刪除actions的相關(guān)操作
  mutations: {
    changeCity (state, city) {
      state.city = city
    }
  }
})

講到這里其實(shí)就實(shí)現(xiàn)了vuex的數(shù)據(jù)一個(gè)設(shè)置以及顯示的一些操作,但是我們更具當(dāng)前的產(chǎn)品需求我們還是需要完善一下頁(yè)面跳轉(zhuǎn)。
之前我們實(shí)現(xiàn)頁(yè)面跳轉(zhuǎn)是通過(guò)
1.router-link 的to屬性來(lái)實(shí)現(xiàn)
2.那么還有一種通過(guò)js 來(lái)實(shí)現(xiàn)頁(yè)面跳轉(zhuǎn)的$router.push
那么我們希望我們?cè)谶x擇完城市后,能自動(dòng)跳轉(zhuǎn)到首頁(yè),那么

this.$router.push('/')

知識(shí)點(diǎn)6 vuex的高級(jí)使用以及l(fā)ocalStorage

store/index.js文件的拆分和localStorage的應(yīng)用

在上面使用vuex中我們給city設(shè)置了一個(gè)初始值:'上海',但是當(dāng)我們切換完城市后,返回首頁(yè),如果我們刷新首頁(yè),那么我們選擇的城市就又變回為了默認(rèn)值:'上海',那么針對(duì)這種情況,我們需要引入本地緩存localStorage,但是呢,有些瀏覽器會(huì)屏蔽localStorage的一些東西,為了程序的健壯性,減少?zèng)]必要的瀏覽器異常,所以在對(duì)localStorage進(jìn)行相關(guān)操作的時(shí)候,我們先進(jìn)行一層try catch的操作

//store/index.js
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) {}
    }
  }
})

寫到這里我們發(fā)現(xiàn),將來(lái)如果我們業(yè)務(wù)比較復(fù)雜的話,store/index.js會(huì)變的越來(lái)越龐大,那么這不是我們希望看到的,所以我們要對(duì)store/index.js進(jìn)行拆分。
那么如何進(jìn)行拆分呢?
store/index.js 只是一個(gè)總文件,而這個(gè)總文件包含很多部分:state、actions、mutations等等,
那么我們將可以將這些模塊拆分成為:state.js、actions.js、mutations.js
最后再把他們引入到store/index.js文件中
那么,根據(jù)這個(gè)思路咱們接下來(lái)拆分一下store/index.js

//store/state.js

let defaultCity = '北京'
try {
  if (localStorage.city) {
    defaultCity = localStorage.city
  }
} catch (e) {

}

export default {
  city: defaultCity
}


//store/mutions.js
export default{
  changeCity (state, city) {
    state.city = city
    try {
      localStorage.city = city
    } catch (e) {}
  }
}

那么store/index.js 就變?yōu)榱耍?/p>

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
})

vuex的高級(jí)應(yīng)用以及針對(duì)項(xiàng)目當(dāng)中的優(yōu)化

我們上面調(diào)用城市的時(shí)候是通過(guò){{this.$store.state.city}}來(lái)實(shí)現(xiàn)的
如果這么寫的話,略顯頁(yè)面比較冗余。那么有沒(méi)有其他方法會(huì)比較簡(jiǎn)單一些呢?
vuex幫我們封裝了一些方法和aip有一個(gè)mapState的方法就可以幫我們實(shí)現(xiàn),那么應(yīng)該如何使用呢?

import { mapState } from 'vuex'

//第一種通過(guò)數(shù)組方法獲取
computed: {
    ...mapState(['city']) //這樣就把把store中的city值獲取到
}
//第二種通過(guò)對(duì)象方法獲?。ㄆ鹨粋€(gè)別名)
computed: {
    ...mapState({
      currentCity: 'city'
    }) //這樣就把把store中的city值獲取到
}

//如果是第一種方法獲取的
將原來(lái)的 {{this.$store.state.city}} 改為 {{this.city}}

//如果是第二種方法獲取的
將原來(lái)的 {{this.$store.state.city}} 改為 {{this.currentCity}}

獲取vuex中store的數(shù)據(jù)我們可以通過(guò)mapState方法,那么設(shè)置vuex數(shù)據(jù)呢?
我們可以通過(guò)vuex給我們提供的mapMutations方法,那么如何實(shí)現(xiàn)呢?

import {mapMutations} from 'vuex'

methods: {
    handleCityClick (city) {
       //this.$store.commit('changeCity', city) 改為下面:
       this.changeCity(city)
       this.$router.push('/')
    },
    ...mapMutations(['changeCity'])
}

講的這里我們使用了vuex給我們提供的state、actions、mutations,我們登錄vue官網(wǎng),我們發(fā)現(xiàn)vuex還給我們提供了兩個(gè)一個(gè)是getter、另一個(gè)是module
那么我們來(lái)看一下getter的使用

//store/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,
  getters: {
      doubleCity (state) {
          return state.city + ' ' + state.city
      }
  }
})

那么頁(yè)面上應(yīng)該如何使用或者調(diào)用呢?

import { mapGetters } from 'vuex'

computed: {
    ...mapGetters(['doubleCity'])
}

//頁(yè)面上調(diào)用
{{this.doubleCity}}

那么我們此時(shí)會(huì)想,這有什么用處呢?因?yàn)閙apState就可以實(shí)現(xiàn)的方法,我為什么還要使用mapGetters來(lái)實(shí)現(xiàn)呢?
其實(shí)呢,我們發(fā)現(xiàn)getters方法有點(diǎn)類似vue組件當(dāng)中的computed方法,他可以把我們state值進(jìn)行處理后返給我們一個(gè)新值,從來(lái)來(lái)避免一些數(shù)據(jù)的冗余。
getter講完了,那么module我們?cè)谑裁辞闆r下去使用呢?
因?yàn)槲覀冊(cè)趕tore/index.js中 只寫了city相關(guān)的(state、actions、mutations)等等操作,當(dāng)我們?cè)趯?shí)際開(kāi)發(fā)的過(guò)程中,我們肯定不單單只有city這一個(gè)模塊的,如果有很多頁(yè)面的功能模塊的話,我們拆分的state.js、actions.js、mutations.js會(huì)變得很臃腫的,這不是我們期盼看到的。
所以我們通過(guò)module模塊對(duì)我們的功能模塊進(jìn)行進(jìn)一步的拆分,每個(gè)功能模塊包含自己的(state、actions、mutations等等)。如下面例子:

const moduleA = {
  state: { ... },
  mutations: { ... },
  actions: { ... },
  getters: { ... }
}

const moduleB = {
  state: { ... },
  mutations: { ... },
  actions: { ... }
}

const store = new Vuex.Store({
  modules: {
    a: moduleA,
    b: moduleB
  }
})

store.state.a // -> moduleA 的狀態(tài)
store.state.b // -> moduleB 的狀態(tài)

知識(shí)點(diǎn)7 使用keep-alive優(yōu)化網(wǎng)頁(yè)
keep-alive是個(gè)抽象組件(或稱為功能型組件),實(shí)際上不會(huì)被渲染在DOM樹(shù)中。它的作用是在內(nèi)存中緩存組件(不讓組件銷毀),等到下次再渲染的時(shí)候,還會(huì)保持其中的所有狀態(tài),并且會(huì)觸發(fā)activated鉤子函數(shù)。因?yàn)榫彺娴男枰ǔ3霈F(xiàn)在頁(yè)面切換時(shí),所以常與router-view一起出現(xiàn):

//app.vue
 <keep-alive>
    <router-view/>
</keep-alive>

如此一來(lái),每一個(gè)在router-view中渲染的組件,都會(huì)被緩存起來(lái)。
如果只想渲染某一些頁(yè)面/組件,可以使用keep-alive組件的include/exclude屬性。include屬性表示要緩存的組件名(即組件定義時(shí)的name屬性),接收的類型為string、RegExp或string數(shù)組;exclude屬性有著相反的作用,匹配到的組件不會(huì)被緩存。假如可能出現(xiàn)在同一router-view的N個(gè)頁(yè)面中,我只想緩存列表頁(yè)和詳情頁(yè),那么可以這樣寫:

<keep-alive :include="['Home', 'City']">
  <router-view />
</keep-alive>

那么針對(duì)咱們這個(gè)項(xiàng)目,當(dāng)我們?cè)黾由蟢eep-alive屬性后,當(dāng)我們?cè)L問(wèn)過(guò)的頁(yè)面請(qǐng)求過(guò)后,再去請(qǐng)求的時(shí)候,那么就不會(huì)再去觸發(fā)ajax請(qǐng)求,而在此項(xiàng)目中首頁(yè)的數(shù)據(jù)變更是需要我們切換不同的城市來(lái)實(shí)現(xiàn)變更的,也就是當(dāng)城市只要變更我們就需要對(duì)首頁(yè)數(shù)據(jù)進(jìn)行一次請(qǐng)求。那么我們應(yīng)該如何更新首頁(yè)的數(shù)據(jù)呢?

//通過(guò)vuex的mapState屬性我們可以獲取 city的值
import { mapState } from 'vuex'
 computed: {
    ...mapState(['city'])
  },

//通過(guò)ajax的入?yún)?lái)請(qǐng)求不同城市的數(shù)據(jù)
getHomeInfo () {
      axios.get('/api/index.json?city=' + this.city).then(this.getHomeInfoSucc)
},

//當(dāng)我們觸發(fā)了keep-alive屬性后,那么就會(huì)多出一個(gè)activated的生命周期鉤子
//通過(guò)城市的變更,我們?cè)谶@個(gè)生命周期函數(shù)中再去請(qǐng)求接口,入?yún)⒂米钚碌某鞘校敲丛趺磥?lái)區(qū)分城市的變更呢?
//data里面定義一個(gè)新的字段lastCity
data () {
    return {
      lastCity: '',//定義
      swiperList: [],
      iconList: [],
      recommendList: [],
      weekendList: []
    }
  },


//如果當(dāng)前的city和最后一次請(qǐng)求的城市不一致,那么我們把最后一次請(qǐng)求的城市賦值為最新的城市,且用最新的城市作為接口請(qǐng)求的入?yún)?activated () {
    if (this.lastCity !== this.city) {
      this.lastCity = this.city
      this.getHomeInfo()
    }
}

更多

上一篇:第9章 Vue項(xiàng)目開(kāi)發(fā)之首頁(yè)
下一篇:第11章 Vue項(xiàng)目開(kāi)發(fā)之詳情
全篇文章:Vue學(xué)習(xí)總結(jié)
所有章節(jié)目錄:Vue學(xué)習(xí)目錄

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容