一、Vuex是什么
1、简介
组件中包含视图(模板template)、双向绑定的数据(data)、以及一些方法(methods),这3个都写在同一个组件(component)里面, 一般视图(View)触发方法动作(Actions),动作影响数据状态(State), 数据状态的改变又反应到视图(View)上来,这样在一个组件内就形成了一个闭环。即当前组件的视图使用当前组件的数据,当前组件的动作(方法)只修改当前组件的数据,总体来说只能自己跟自己玩,不能多个组件相互玩。
我们有这样两个需求:
- 多个视图依赖于同一状态。
- 来自不同视图的行为需要变更同一状态。
对于问题一,传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。
对于问题二,我们经常会采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码。
为了解决以上问题,我们把组件的共享状态(data)抽取出来,以一个全局单例模式管理, 这样所有组件在任意时候都可以访问全局状态,当在任意组件中修改全局状态,所有引用全局状态的视图也会随之改变(响应式)。这就是Vuex的功能。
简单来说Vuex在一个工程中只能有一个全局实例对象(Store),也就是该实例对象是整个应用级别的, 一个对象就包含了全部的应用层级状态。 store被称为仓库,用于盛放全局状态的容器。任何组件都能访问全局对象的数据(State),任何组件都可以修改全局对象的数据。这就是我们平常说的设计模式中的“单例模式”。
2、全局单例 Store 伪代码
class Vuex.Store {
public Object state;
// 同步操作,接收参数 (state)
public Object mutations;
// 异步操作, 接收参数 (context)
public Object actions;
// 每个函数可以接收两个参数 (state, getters)
// 相当于是state的计算函数
Public Object getters;
// 模块(module)
public Object modules;
// 命名空间(类似于包package的概念)
boolean namespaced;
// 插件
public Object[] plugins;
// 严格模式
public boolean strict;
/*
* 提交mutation
* 载荷(Payload):就是参数
*/
public void commit(String mutation, Object payload) {
}
/*
* 分发action
* 载荷(Payload):就是参数
*/
public void dispatch(String mutation, Object payload) {
}
}
二、Vuex HelloWorld
1、Vuex安装
npm install vuex --save
显式使用Vuex插件,一般写在src/main.js中,或者写在其它js中然后再在main.js中引入
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
2、多组件共享全局状态示例
分别在Foo.vue和Bar.vue中改变全局属性count值,然后在App.vue中显示count修改后的值。
(1)定义全局单例对象 src/store/store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state, payload) {
state.count += payload.step
},
decrement: state => state.count--,
}
})
定义一个全局实例对象(Vuex.Store):
-
该对象的状态(state)就是全局属性, 类似于组件的data,每个组件都可以访问和修改属性。
-
可变的(mutations)类似于组件中的methods, mutations中的每个方法称作为 mutation handler,用来修改state中的值,方法的参数可以有两个(state, payload) state表示全局的单例对象,payload(载荷)也就是参数,调用时可以传递参数,该参数是可选的。
使用Mutation时需遵守的一些规则:
-
最好提前在你的 store 中初始化好所有所需属性。
-
当需要在对象上添加新属性时,你应该使用 Vue.set(obj, ‘newProp’, 123), 或者以新对象替换老对象
-
Mutation 必须是同步函数
(2)在src/main.js中导入store.js并作为Vue的选项
import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store/store'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
store,
router,
components: {
App },
template: '<App/>'
})
将store作为Vue的选项,这样Vue会将store“注入”到每一个子组件中,也就是说每个组件都可以通过this.$store来访问全局单例对象store。
(3)Foo.vue
<template>
<div>
Foo.vue <button @click="increment">+</button>
</div>
</template>
<script>
export default {
name: 'Foo',
methods: {
increment () {
this.$store.commit('increment', {
step: 10
})
}
}
}
</script>
调用store中的mutations方法只能通过提交的方式this.$store.commit('方法名', 负载参数)
这一种形式来调用,而不能使用this.$store.方法名
这种普通的的对象.方法()
方式直接调用。如果感觉这种方式麻烦,Vuex提供了一种将Mutations映射(map)为methods的方式, 然后在使用时直接调用method就会自动帮你commit。
mapMutations() 函数,它接收一个参数,参数类型可以是数组也可以是对象:
- 数组类型:当使用方法时方法名称和Mutation的名称一样时使用数组类型。
- 对象类型:当使用方法时方法名称不想和Mutation的名称一样,可以对method起一个新的名称
<template>
<div>
Foo.vue <button @click="add({step: 10})">+</button>
</div>
</template>