vuex
一、vuex是什么
vue官方提供的状态管理器。用于处理全局的数据共享问题的。
二、vuex的仓库中的五大核心概念
- state 专门存共享数据的地方
- getter 可以针对现有的 state 数据或者其余的 getter 做的一个二次计算。可以理解为仓库的计算属性
- mutation 唯一能够修改 state 数据的东西,不允许写异步代码
- action 它里面可以写异步代码,它如果要修改 state 数据,是通过去调用 mutation
- module 仓库模块的拆分
三、如何使用
-
项目中安装 vuex
$ npm install vuex -
创建 src/store.js 文件。这个文件用来生效仓库的实例
-
需要在 src/main.js 文件的 new Vue() 的位置去配置 store 选项,选项值是上一个步骤中暴露出来的 store 的实例
四、如何分辨是否已经配置好 vuex 的仓库了呢
查看 vueDevtools 就可以
五、组件中如何使用 state 与 getter
方案一、使用挂载到 Vue原型上的 $store 对象。这个 $store 就是 new Vuex.Store() 生成的仓库实例对象
方案二 (推荐)、使用 computed
<script>
export default {
computed: {
curCity() {
return this.$store.state.curCity;
},
cart() {
return this.$store.state.cart;
}
}
}
</script>
方案三(推荐、就是方案二的一个优雅写法)、使用 vuex 提供的辅助函数
-
组件中从 vuex 中引入你需要的辅助函数
-
调用辅助函数,并将其返回的值赋给组件的 computed 选项
<script>
// 引入 辅助函数 。采用类似 解构赋值的方式
import { mapState } from 'vuex'
export default {
// mapState 返回值是一个 对象
computed: mapState(['curCity', 'cart'])
}
</script>
mapState 与 mapGetters 的语法
// mapState 接收一个数组做为参数,参数中的每一项,就是在仓库中的 state 数据
mapState([state1, state2, state3])
// mapGetters 接收一个数组做为参数,参数中的每一项,就是在仓库中的 getter 数据
mapGetters([getter1, getter2])
方案二与方案三效果一样,但是在 VueDevtools 中的表现有稍微一点不同
方案二时,插件中显示的是 computed
方案三时,插件中显示的是 vuexbindings
方案二与方案三效果一样,一般我们更推荐大家使用方案三。但是在什么情况下要使用方案二呢?
希望组件中的数据与仓库中的数据用不同的名字的时候。采用方案二
使用方案三时,我们组件自身还有一些自己的computed数据,该如何办?
将 mapState() 使用 ... 做展开
六、组件中如何去修改仓库中的 state 与 getter 数据呢
首先明确一点。state 可以修改。getter 不能修改。
步骤:
1. 仓库中要提供 对应 state 修改的 mutation 函数
2. 在组件中去调用 mutation
调用 mutation 的三套方案:
方案一、直接使用 vuex 绑定到 vue 原型上的 $store 这个对象的 commit() 方法
方案二、在组件中先定义一个函数,函数名跟后续要调用的mutaion名字保持一致,函数内部使用方案一
方案三、使用 mapMutations 辅助函数
// mapMutations的语法
// 接收一个数组作为参数,数组中的每一项是一个 mutation 的名字
mapMutations([mutation1, mutation2, mutation3])
示例代码
{
methods: mapMutations(["SETCURCITY", "ADDCART"])
}
示例代码转换就是如下的代码效果
{
methods: {
SETCURCITY(payload) {
this.$store.commit('SETCURCITY', payload)
},
ADDCART(payload) {
this.$store.commit('ADDCART', payload)
}
}
}
为了结合组件自身的函数,所以 mapMutations 也需要使用展开运算符来使用
所有的 mutation 执行,都能够在 VueDevtools 去看到
为什么 vuex 中的 state 必须使用 mutation 来修改呢
- 为了以一种可以预见的方式去修改数据,不至于让数据难以理解。
- 为了实现时间旅行。
七、使用 action 异步的修改 state 数据
首先需要知道,mutation 里面只允许同步的去修改 state 数据。(虽然在mutation中可以异步的去修改state数据不会报错,但是会导致时间旅行等机制没有效果)
如果异步的修改的化,有两个大方案
- 不涉及action。在组件上异步代码走完之后再去调用 mutation
- 使用 action
使用 action。首先需要在 actions 选项中定义 action 函数
注意:action 中不能直接去修改 state,要修改是通过 context.commit() 去执行某个 mutation 来修改
方案一、直接使用 vuex 绑定到 vue 原型上的 $store 这个对象的 dispatch() 方法
// dispatch 语法
// actionName - 要调用action的名字
// payload - 要传递这个 action 的参数
this.$store.dispatch(actionName, payload)
方案二、在组件中先定义一个函数,函数名跟后续要调用的 action 名字保持一致,函数内部使用方案一
方案三、使用 mapActions 这个辅助函数
// mapActions 的语法
// 接收一个数组作为参数,数组中的每一项是一个 action 的名字
mapActions([action1, action2, action3])
示例代码
{
methods: mapActions(["SYNCSETCURCITY"])
}
转换之后如下所示
{
methods: {
SYNCSETCURCITY(payload) {
this.$store.dispatch('SYNCSETCURCITY', payload)
}
}
}
vuex 的 module
一、什么时候需要在 vuex 中使用 module
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。
为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:
项目越做越大,功能点越写越多。需要使用 vuex 共享的数据越来越庞大时,就需要使用 module 来进行仓库模块拆分啦。
示例代码
// 拆分的仓库子模块A
const moduleA = {
state: { ... },
mutations: { ... },
actions: { ... },
getters: { ... },
// 仓库子模块也可以继续去做拆分,但是没必要搞这么复杂
modules: {
aa,
ab,
}
}
// 拆分的仓库子模块B
const moduleB = {
state: { ... },
mutations: { ... },
actions: { ... }
}
// 仓库根模块
const store = new Vuex.Store({
// 通过 modules 选项配置子模块
modules: {
// key: value
// key - 仓库子模块的名字
// value - 对应的仓库子模块的对象
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
二、仓库拆分子模块之后,没有设置命名空间。有一些问题的
默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的
- 多个仓库子模块中的 getter 不能同名,否则会报错
- 多个仓库子模块中的 mutation 如果同名的化,组件调用这个 mutation 时。都会被触发, 会有污染的风险
- 多个仓库子模块中的 action 如果同名的化,组件调用这个 action 时。都会被触发, 会有污染的风险
三、由于有上面的这种问题存在,所以推荐仓库子模块都设置上命名空间。
1. 如何设置呢?
给 仓库子模块的 那个对象配置一个 namespaced 属性。属性值为 true
2. 设置之后的改变是什么?
模块内部的 action、mutation 和 getter 是注册在自己命名空间的
3. 设置了命名空间之后如何在组件中使用呢
基本使用步骤不变,只是要处理处理命名空间
下面的 xx 代表着某个仓库子模块的名字
1. 获取某个仓库子模块中的 state
// 1. 直接通过 $store
this.$store.state.xx
// 2. computed
computed: {
name () {
return this.$store.state.xx.name
}
}
// 3. mapState
computed: {
...mapState('xx', [state1, state2, state3])
}
// 4. mapState 的转换
computed: {
state1() {
return this.$store.state.xx.state1
}
}
// !如果要在组件中同时拿到多个仓库子模块的同名state数据,不要使用 mapState 请使用方案二 !
2. 获取 某个仓库子模块中的 getter
// 1. 直接通过 $store
this.$store.getters['ma/firstName']
// 2. computed
computed: {
firstName() {
return this.$store.getters['ma/firstName']
}
}
// 3. mapGetters
computed: {
...mapGetters('xx', [getter1, getter2])
}
// 4. mapGetters 的转换
compouted: {
getter1() {
return this.$store.getters['xx/getter1']
}
}
3. 提交 某个仓库子模块中的 mutation
// 1. 直接通过 $store
this.$store.commit('ma/SET_NAME', payload)
// 2. methods
methods: {
SET_NAME(payload) {
this.$store.commit('ma/SET_NAME', payload)
}
}
// 3. mapMutations
methods: {
...mapMutations('xx', [mutation1, mutation2])
}
// 4. mapMutations 的转换
methods: {
mutation1(payload) {
this.$store.commit('xx/mutation1', payload)
}
}
4. 派发 某个仓库子模块中共的 action
// 1. 直接通过 $store
this.$store.dispatch('ma/SYNC_SET_NAME', payload)
// 2. methods
methods: {
SYNC_SET_NAME(payload) {
this.$store.dispatch('ma/SYNC_SET_NAME', payload)
}
}
// 3. mapActions
methods: {
...mapActions('xx', [action1, action2])
}
// 4. mapActions 的转换
methods: {
action1(payload) {
this.$store.dispatch('xx/action1', payload)
}
}
各部分内容在文件夹中的显示
modules/a.js
// 拆分出来的仓库子模块A
// 仓库子模块其实就是一个包含有 state 、getters、mutations、actions 的对象
export default {
// 命名空间
namespaced: true,
state: {
name: "张三",
},
mutations: {
SET_NAME(state, payload) {
console.log("a");
state.name = payload;
},
},
actions: {
SYNC_SET_NAME(context, payload) {
console.log("a-a");
setTimeout(() => {
// 调用 SET_NAME 这个 mutation 来修改 state 数据
context.commit("SET_NAME", payload);
}, 2000);
},
},
getters: {
firstName(state) {
return state.name.substr(0, 1);
},
},
};
modules/b.js
// 拆分出来的仓库子模块B
export default {
// 命名空间
namespaced: true,
state: {
name: "李四",
},
mutations: {
SET_NAME(state, payload) {
console.log("b");
state.name = payload;
},
},
actions: {
SYNC_SET_NAME(context, payload) {
console.log("b-b");
setTimeout(() => {
// 调用 SET_NAME 这个 mutation 来修改 state 数据
context.commit("SET_NAME", payload);
}, 2000);
},
},
getters: {
firstName(state) {
return state.name.substr(0, 1);
},
},
};
index.js
// 仓库根模块
// 1. 引入 Vue
import Vue from "vue";
// 2. 引入 Vuex
import Vuex from "vuex";
// 引入拆分出来的仓库子模块
import moduleA from "./modules/a";
import moduleB from "./modules/b";
// 3. 调用
Vue.use(Vuex);
// 4. 生成 仓库 实例对象
const store = new Vuex.Store({
// 只配置 modules
modules: {
ma: moduleA,
mb: moduleB,
},
});
// end. 暴露 store
export default store;
父模块state、mutations、getters、actions内容过多时,将这四部分抽离出去,方便管理
子模块state、mutations、getters、actions内容过多时,将这四部分抽离出去,方便管理
学习笔记