Vue2.5 旅游项目实例17 城市选择页-使用Vuex实现数据共享

创建分支:city-vuex

拉取到本地并切换分支:

git pull
git checkout city-vuex

我们要做的功能是在城市列表页,选择某一个城市后,首页右上角的城市也跟着变化。

安装Vuex:

npm install vuex --save

在src目录下,创建store文件夹,新建index.js文件:

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

Vue.use(Vuex)

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

然后打开main.js文件,添加:

import Store from './store'

new Vue({
  el: '#app',
  router,
  store,
  components: { App },
  template: '<App/>'
})

打开Home.vue文件,可以看到给 home-hander 组件传递了一个city,现在我们改为:

<home-header></home-header>

现在刷新首页,右上角的北京没有了,只有一个下箭头。

然后打开Header.vue文件:

<div class="header-right">{{this.$store.state.city}} <i class="iconfont arrow-icon">&#xe64a;</i></div>

<script>
export default {
  name: 'HomeHeader'
}
</script>

这时首页右上角的北京又显示出来了。this.$store.state.city 取的是store里存储的默认值:北京

再打开city下的List.vue文件,把当前城市改为读取stroe:

<div class="button">{{this.$store.state.city}}</div>

这时候我们可以把store/index.js中 state.city 的默认值改为:上海

然后可以看到首页和列表也中的当前城市,都变为了上海。

下面我们希望点击热门城市,可以跟着变化,给每个热门城市添加点击事件:

<div class="button-wrapper" v-for="item in hotCity" :key="item.id"
         @click="handleCityClick(item.name)">

<script>
export default {
  methods: {
    // 热门城市点击事件
    handleCityClick (city) {
      this.$store.dispatch('changeCity', city)
    }
  },
}
</script>

然后打开store/index.js文件:

根据上图,首先组件调用 actions,actions 调用 mutations, mutations 去改变数据。

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

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    city: '北京'
  },
  actions: {
    changeCity (context, city) {
      context.commit('changeCity', city)
    }
  },
  mutations: {
    changeCity (state, city) {
      state.city = city
    }
  }
})

这时候点击热门城市,当前城市跟着变化,首页右上角的城市也变化。这就实现了首页和城市列表页的数据共享。

但是我们刚才这个过程里没有任何的异步操作,而且操作也很简单,不是一些批量的数据操作,所以组件没必要去调用acions,可以直接去调用 mutations,所以我们可以修改为:

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

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    city: '北京'
  },
  // actions: {
  //   changeCity (context, city) {
  //     context.commit('changeCity', city)
  //   }
  // },
  mutations: {
    changeCity (state, city) {
      state.city = city
    }
  }
})

然后Lits.vue修改为:

// 热门城市点击事件
    handleCityClick (city) {
      // this.$store.dispatch('changeCity', city)
      this.$store.commit('changeCity', city)
    }

OK,功能一样可以实现。

下面把城市列表也添加上点击事件:

<div class="item border-bottom" v-for="v in item" :key="v.id" @click="handleCityClick(v.name)">
          {{v.name}}
</div>

当然搜索的时候点击城市也要添加点击事件,打开Search.vue:

<li class="search-item border-bottom" @click="handleCityClick(item.name)"
       v-for="item in list" :key="item.id">

<script>
export default {
  methods: {
    // 切换城市点击事件
    handleCityClick (city) {
      this.$store.commit('changeCity', city)
    }
  },
}
</script>

OK,功能实现了。

下面就是在点击切换城市后,直接跳回到首页:

handleCityClick (city) {
      this.$store.commit('changeCity', city)
      this.$router.push('/')
}

现在点击城市后,可以切换当前城市并且返回到首页。

Vuex的高级使用和localStorage

继续上面的代码,当我们点击城市切换,并返回到首页时,这时候都没问题,但是我们刷新页面,这时候右上角的城市又变为了默认值北京。这时候我们就需要用到 localStorage 本地存储。

打开stroe/index.js文件:

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

这时切换城市后,在怎么刷新,也不会变了。

建议:

当使用 localStorage 的时候,建议大家在外层包裹一个try catch,因为在某些浏览器,如果用户关闭了本地存储功能或者使用了隐身模式时,使用 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 {
        localStorage.city = city
      } catch (e) {
      }
    }
  }
})

刷新,功能还是一样的。

这时候我们发现store/index.js的文件开始复杂起来了,这时候我们可以进行拆分,新创建一个state.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) {
    }
  }
}

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: state,
  mutations: mutations
})

这样我们就拆分为了几个部分。

小bug:当我们在城市列表中,点击文字多的城市,比如“阿拉善盟”,这时首页的样式有点变形了。

打开home下的Header.vue文件,修改下样式:

.header-right
    padding:0 .1rem
    min-width: 1.04rem
    float:right
    text-align: center
    color: #fff

这时候选几个字的城市都可以了。

下面进行代码优化

home文件夹下的Header.vue代码优化:

<div class="header-right">{{this.city}} <i class="iconfont arrow-icon">&#xe64a;</i></div>

<script>
import { mapState } from 'vuex'
export default {
  name: 'HomeHeader',
  computed: {
    // 展开运行符 
    // 把city这个公用数据映射到名字叫做city的计算属性之中
    ...mapState(['city'])
  }
}
</script>

city文件夹下的list.vue代码优化:

<div class="button">{{this.city}}</div>

<script>
import Bscroll from 'better-scroll'
import { mapState, mapMutations } from 'vuex'
export default {
  computed: {
    // 把vuex里面的city这个公用的数据映射到这个组件的计算属性里,映射过来的名字叫做currentCity
    ...mapState({
      currentCity: 'city'
    })
  },
  methods: {
    // 热门城市点击事件
    handleCityClick (city) {
      // this.$store.commit('changeCity', city) 改为下面代码
      this.changeCity(city)
      this.$router.push('/')
    },
    // 有一个mutations叫changeCity,然后把这个mutations映射到组件里一个名字叫changeCity的方法里
    ...mapMutations(['changeCity'])
  },
}
</script>

然后是Search.vue页面:

import { mapMutations } from 'vuex'
export default {
  methods: {
    // 切换城市点击事件
    handleCityClick (city) {
      // this.$store.commit('changeCity', city) 改为下面代码
      this.changeCity(city)
      this.$router.push('/')
    },
    ...mapMutations(['changeCity'])
  },  
}

Vuex核心概念:

State:存放的是公用的数据

Action:一些异步的方法可以写在Action里

Mutation:放的是同步的一些对数据的改变

Getter:当我们需要根据state里的数据,计算出一些新的数据时,我们可以用Getter。有点类似于组件中的computed计算属性的作用。

Module:可以把 store的代码进行分割成模块

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

home下的Header.vue文件:

<div class="header-right">{{this.doubleCity}} <i class="iconfont arrow-icon">&#xe64a;</i></div>

<script>
import { mapState, mapGetters } from 'vuex'
export default {
  name: 'HomeHeader',
  computed: {
    ...mapState(['city']),
    ...mapGetters(['doubleCity'])
  }
}
</script>

这时页面效果图:

提交代码并进行合并:

git add .
git commit -m "vuex实现数据共享和localStorage"
git push

git checkout master
git merge city-vuex
git push

 

posted on 2020-03-20 11:22  JoeYoung  阅读(326)  评论(0编辑  收藏  举报