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"></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"></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"></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