Vuex是什么
Vuex 是状态管理模式,采用集中式存储管理所有组件的状态 (数据),并以相应的规则保证状态 (数据) 以一种可预测的方式发生变化
当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏:
- 多个视图依赖于同一状态 ---- 传参对于多层嵌套的组件非常繁琐,无法在兄弟组件间传递
- 来自不同视图的行为需要变更同一状态 --- 多份拷贝数据的变更和同步通常难以维护
因此,把组件的共享状态抽取出来,以一个全局单例模式管理,在这种模式下,我们的组件树构成了一个巨大的“视图”,不管组件在树的哪个位置,任何组件都能获取状态或者触发行为,通过剥离 组件 和 状态/方法,维持视图和状态间的独立性,代码会变得更 结构化 且 易维护

什么情况使用Vuex
如果应用够简单,最好不要使用 Vuex,一个简单的 store 模式 就足够了
如果需要构建一个中大型单页应用,为了更好地在组件外部管理状态,Vuex 将会成为自然而然的选择
开始
Vuex 应用的核心是 store ( 仓库 ),“store” 基本上就是一个容器,包含着应用中大部分的状态 (state),Vuex 和单纯的全局对象有以下两点不同:
-
Vuex 的状态存储是响应式的,组件从 store 中读取状态,若 store 中的状态发生变化,组件也会得到高效更新
-
不能直接改变 store 中的状态,改变 store 中状态的唯一途径就是显式地提交 (commit) mutation,这样可以方便地跟踪每一个状态的变化,从而能够实现一些工具,以便更好地了解应用
通过 vue create project name 创建项目勾选 vuex,或者在现有项目中 vue add vuex 添加
安装完成,main.js 中多了引入和挂载
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store' //导入创建的store
Vue.config.productionTip = false
new Vue({
router,
store,//挂载store,在任意组件中访问 this.$store property
render: h => h(App)
}).$mount('#app')
src目录下多了个 store/index.js
import Vue from 'vue'
import Vuex from 'vuex' //1.导入模块
Vue.use(Vuex) //2.使用当前的插件
export default new Vuex.Store({ //3.创建实例并抛出
state: { //存储当前的状态
conte: 0
},
mutations: { //声明同步的方法
},
actions: { //声明异步的方法
},
modules: {
}
})
在组件中获取状态
store 内声明的属性,可以通过$store.state直接获取,也可以在需要的组件内通过 computed 属性监听获取,显得麻烦但好处是可以向store提交数据
{{$store.state.count}} //直接获取方法
//--------------------------
computed: { //计算属性获取
count() {
return this.$store.state.count;
}
}
dispatch & commit 触发修改状态
异步方法
组件中声明方法,通过 dispatch(调用) 触发 actions 中声明的方法来对服务器进行数据交互,实现异步方法
methods: {
plusOne() { //1.声明方法
this.$store.dispatch("plusOne");//2.通过dispatch调用actions内的方法
}
}
事件触发组件中的方法,在 store 的 actions 内的方法被调用后触发 mutations 内的方法来修改当前状态
export default new Vuex.Store({
state: {
count: 11//5.状态被更改后,引入此状态的组件都会立即更新
},
mutations: {
plusOne(state) {//4.执行方法并传入state,更改state内的状态
state.count++
}
},
actions: {
plusOne({ commit }) {//3.此方法被调用后,通过commit来调用mutations内的plusOne方法
commit('plusOne')
}
},
modules: {
}
})
此时当前状态被修改,各个没有任何关系的组件中引入的 {{ $store.state.count }} 都会响应这个状态的更新
同步方法
当前操作不需要对服务器进行数据交互时,可以在组件方法中使用 this.$store.commit('plusOne') 直接触发 mutations 内的方法完成状态更改
Getters属性
-
store 内还有个 getters 属性,类似于 store 内的计算属性,如果多个组件都需要统一的计算属性得出新的状态,可以使用这个属性来处理,就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算,getter 接受 state 作为其第一个参数:
getters: { evenOrOdd(state){ return state.count % 2 ? '偶数':'奇数' } } -
Getter 会暴露为
store.getters对象,你可以以属性的形式访问这些值:computed: { evenOrOdd() { return this.$store.getters.evenOrOdd; } } -
Getter 也可以接受其他 getter 作为第二个参数:
getters: { doneTodosCount: (state, getters) => { return getters.doneTodos.length } }store.getters.doneTodosCount // -> 1 -
Getter 还可以接收 rootState 作为第三个参数获取整个store的State,通过
rootState.module模块名.state状态名来获取任意不相关的store的状态getters: { doneTodosCount: (state, getters, rootState) => { return rootState.products.products //第一个product是模块名,第二个是状态名 } }
Vuex辅助函数运用
每次引入都需要 this.$store.state.count / this.$store.dispatch("") / this.$store.commit("")就显得非常繁琐
vuex提供了四种辅助函数,分别对 store 中 state / getters / mutations / actions 进行处理
import { mapState,mapGetters,mapMutations,mapActions } from 'vuex';
mapState & mapGetters
接下来只需要在计算属性中使用对应函数就可以获取到想要的状态
computed:{
...mapState(['count','username']),
...mapGetters(["evenOrOdd"])
}
如果需要引入的属性名 和 store 的状态名不一样可以这样写来对状态进行重命名
computed:{
...mapState({
myCount: 'count',
user: 'username'
})
}
mapMutations & mapActions
这两个的获取方式和上面两个一样,区别在于,上面两个获取的是状态,这两个获取的是方法,所以需要在 methods 中获取,就可以直接在事件中使用了
methods: {
...mapMutations(['plusOne']),
...mapActions(['plusOne'])
}
<button @click='plusOne'> +1 </button>
组件内向store传递数据
方法内传递数据
data() {
return {
amount: 10
}
},
methods: {
plusOne() {
this.$store.dispatch("plusOne",this.amount);
}
}
上面的还有另一种写法:
methods: {
plusOne() {
this.$store.dispatch({
type:"plusOne",
this.amount
});
}
}
辅助函数传递数据
由于辅助函数获取store内的方法,是在事件处直接触发,所以传值也要在事件那里完成,这样反而更简便
<button @click="plusOne(amount)">+1</button>
store获取数据
mutations: {
plusOne(state, amount) {
state.count += amount
}
},
actions: {
plusOne({ commit }, amount) {
commit('plusOne', amount)
}
}
如果传入store的数据是一个对象,可以通过解构赋值获取,在函数内使用
actions: {
plusOne({ commit }, obj) {
console.log(obj), //{amount: 10}
commit('plusOne', obj)
}
}
传递对象给 mutation,并通过解构赋值获得数据
mutations: {
plusOne(state, {amount}) {
state.count += amount
}
},
actions: {
plusOne({ commit },obj) {
commit('plusOne', obj)
}
}
同步方法便可直接省略actions这一步
Module属性
当应用变得非常复杂时,store 对象就有可能变得相当臃肿
Vuex 允许将 store 分割成模块 ( module ),每个模块拥有自己的 state、mutation、action、getter 甚至是嵌套子模块从上至下进行同样方式的分割:
创建Vuex模块
import vue from 'vue'
import vuex from 'vuex'
import mod1 from './mod1.js' //store的index.js中引入分割后的模块
vue.use(vuex);
export default new vuex.Store({
modules:{ //配置模块
mod1
}
});
mod1.js:在store下新建module,放入其中
export default {
state:{},
mutations:{},
actions:{},
getters:{}
}
读取模块数据
在组件中通过 $store.state.mod1.count 读取到对应模块内的state
{{$store.state.mod1.count}}
命名空间
默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的,这样使一个组件内调用 mutation 或 action 时,多个模块能够同时作出响应
如果希望模块具有更高的封装度和复用性,可以通过添加 namespaced: true 的方式使其成为带命名空间的模块,当模块被注册后,它的所有 getter、action 及 mutation 都会自动根据模块注册的路径调整命名,state内的状态已经嵌套了,namespaced不会对其产生影响
export default {
namespaced: true,
state:{},
mutatons:{},
actions:{},
getters:{}
}
获取命名空间的方法
通过方法获取:
export default {
methods: {
plusOne(){
this.$store.dispatch("mod1/plusOne");
}
}
}
通过辅助函数获取:
import { mapGetters } from 'vuex' //组件内引入辅助函数
export default {
methods: {
...mapActions("mod1", ["plusOne"]) //函数内第一个值为vuex模块名,第二个为模块内方法
}
}