在我们的程序中,City.vue和Home.vue是没有公用的父级组件的,因此没办法使用父级组件作为中转站的方式进行数据的传递。在这样的情况下我们可以使用数据框架Vuex辅助进行数据传递。

Vuex的设计理念是把数据放在公共的存储空间进行存储,当某个组件改变了数据,其他组件也可以感知到。
state:存放公用数据。
- 组件改变数据先使用dispatch调用Actions做异步处理或批量同步操作,再使用commit调用Mutations改变公用数据的值。
- 有时也可以直接从State到Mutations,跳过Actions。
一、安装Vuex
npm install vuex --save
二、使用Vuex
①组件调用actions来调用mutations,以此改变state值
在src目录下创建store文件夹,创建index.js文件,引入Vuex,并创建store。
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state:{},
mutations:{},
})
在main.js中引入store,并在引入根vue实例时把store传入进去。
import store from "./store/index.js"
new Vue({
el: '#app',
// 键和值一样,写一个即可
router,
store,
components: { App },
template: '<App/>'
})
如下图,在首页的header区域,我们希望第一次访问网站的人默认显示城市为北京。

我们在store/index.js内对state数据进行定义。这样在其他组件中要使用时可以使用{{this.$store.state.city}}来表示
let defaultCity = "北京"
// 使用localstorage最好加入try catch,这是为了防止出现异常,因为用户可能关闭本地存储的功能导致出错
try{
if (localStorage.city){
defaultCity = localStorage.city
}
} catch(e) {}
export default new Vuex.Store({
state:{
city:defaultCity
},
})
在点击其他城市时,我们希望实现当前位置也发送改变的功能。

因此我们在list.vue中为列表绑定一个click,同时在methods中定义handleCityClick事件。
@click="handleCityClick(item.name)
handleCityClick(city){
// 可以省略action步骤,直接由commit调用mutation
this.$store.dispatch("changeCity",city)
}
仅仅在list.vue中的methods里定义了handleCityClick是无法起作用的,因为触发的changeCity事件还未定义,我们需要在store中定义changeCity函数。
export default new Vuex.Store({
actions:{
changeCity(ctx,city){
// 借助ctx拿到commit,用mutation的changeCity来改变city的值。
ctx.commit('changeCity',city)
}
},
})
这样Actions就接收到了传递来的数据(城市),需要调用Mutations来改变state的值。因此我们还需要在store中定义Mutations。
export default new Vuex.Store({
mutations:{
changeCity(state,city){
// 让state的city等于传入的city
state.city = city
try {
localStorage.city=city
} catch (e) {}
}
}
})
②组件直接调用mutations来改变state值
在list.vue中handleCityClick函数可以不用dispatch调用Actions,而直接由commit调用Mutations。
handleCityClick(city){
// 可以省略action步骤,直接由commit调用mutation
this.$store.commit("changeCity",city)
}
因此,store定义可以删去Actions部分。
export default new Vuex.Store({
state:{
city:defaultCity
},
//actions:{
// changeCity(ctx,city){
// // 借助ctx拿到commit,用mutation的changeCity来改变city的值。
// ctx.commit('changeCity',city)
// }
//},
mutations:{
changeCity(state,city){
// 让state的city等于传入的city
state.city = city
try {
localStorage.city=city
} catch (e) {}
}
}
})
③当index.js内的内容越来越多时,我们可以把state和mutations拿出来单独写成代码文件。
state.js
let defaultCity = "北京"
// 使用localstorage最好加入try catch,这是为了防止出现异常,因为用户可能关闭本地存储的功能导致出错
try{
if (localStorage.city){
defaultCity = localStorage.city
}
} catch(e) {}
export default {
city: defaultCity
}
mutations.js
export default{
changeCity(state,city){
// 让state的city等于传入的city
state.city = city
try {
localStorage.city=city
} catch (e) {}
}
}
在index.js内导入以上两个文件
import state from "./state"
import mutations from "./mutations"
④mapState, mapMutations, mapGetters
- 在其他组件中通过{{this.$store.state.city}}使用数据书写麻烦,利用mapState简写
在使用数据的组件<script>部分加入以下代码,把state的数据映射到名为city的计算属性中。那么在使用数据时,可以不写{{this.$store.state.city}},直接写作{{this.city}}
import mapState from 'vuex'
export default{
name:'HomeHeader',
computed: {
// 把city的数据映射到名字叫做city的计算属性中
...mapState(['city']),
...mapGetters(['doubleCity'])
}
}
也可以写作以下形式,使用{{this.currentCity}}使用数据
import mapState from 'vuex'
export default{
name:'CityList',
computed:{
// 以下两种写法均可,可以是数组也可以是对象
// ...mapState(['city'])
...mapState({
currentCity:'city'
}),
},
}
- 点击城市时,通过 this.$store.commit 派发mutation,书写复杂,可以利用mapMutations简写
methods:{
handleCityClick(city){
// 可以省略action步骤,直接由commit调用mutation
this.$store.commit("changeCity",city)
},
}
首先在需要派发mutation的组件中<script>部分需要引入mapMutations
import { mapState, mapMutations} from 'vuex'
然后在methods里应用mapMutations,原methods中handleCityClick函数如下:
methods:{
handleCityClick(city){
this.$store.commit("changeCity",city)
this.$router.push("./")
},
应用mapMutation之后handleCityClick函数:
(mapMutation把mutation映射到组件中名为changeCity的方法里)
methods:{
...mapMutations(['changeCity']),
handleCityClick(city){
// 引入mapMutations之后可以简写
this.changeCity(city)
this.$router.push("./")
},
- 当需要利用state里的数据进行计算出新的数据时可以使用mapGetters,避免数据冗余
export default new Vuex.Store({
state,
mutations,
// gettters的作用和computed作用类似
// 当需要利用state里的数据进行计算出新的数据时可以使用getters
getters:{
doubleCity (state) {
return state.city+' ' +state.city
}
}
})
在需要使用数据的组件<script>中引入mapGetters,并在computed部分进行映射
import mapGetters from 'vuex'
export default{
name:'HomeHeader',
computed: {
// 把city的数据映射到名字叫做city的计算属性中
...mapState(['city']),
...mapGetters(['doubleCity'])
}
}
在组件中可以通过{{this.doubleCity}}使用数据
⑤模块化的Vuex(Modules)
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:
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 的状态
store.state.b // -> moduleB 的状态