系统性学习vue-vuex
- 理解vuex
- vuex工作原理
- 搭建vuex环境
- 案例
- Vuex的开发者工具使用
- getters配置项
- mapState与mapGetters
- mapActions和mapMutations
- vuex模块化+namespace
理解vuex
概念: 专门在Vue中实现集中式状态(数据)管理的一个Vue插件,对vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信
兄弟组件间需要共享数据
vuex是不属于任何组件的一个存储区域,所有组件都可以对其数据进行获取和更改
vuex工作原理
vuex有三个重要部分组成:Actions、Mutations、State,他们都是对象类型,且都由store来管理
如果不需要请求服务器数据或一些处理业务逻辑,那也可以直接从vc到Mutations
搭建vuex环境
- 安装
npm i vuex
注意:如果使用的是vue2,那就要下载vuex3,而目前默认是下载vuex4,vuex4是要在vue3中使用npm i vuex@3
- 引入并使用vuex(main.js中)
import Vuex from "vuex";
Vue.use(Vuex)
引入并使用后vm和vc就都可以看到$store
这一属性了 - 创建store
有两种写法:
一种是src下创建vuex文件夹,里面再创建store.js
另一种是src下创建store文件夹,里面再创建index.js(官网使用)
// /src/store/index.js
// 该文件用于创建Vuex中最为核心的store
// 引入vuex
import Vuex from "vuex";
// 准备actions 用于响应组件中的动作
const actions = {};
// 准备mutations 用于操作数据(状态)
const mutations = {};
// 准备state 用于存储数据
const state = {};
// 创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state,
});
// 暴露store
// export default store; //改为简写
- 引入store并添加配置项(main.js中)
import store from "./store";
new Vue({
render: (h) => h(App), //将App组件放入容器中
// 配置store
store,
//....
}).$mount("#app"); //绑定模板
- 运行,报错
意思是要在创建store实例之前use(Vuex)
main.js中 我们import引入store
import Vuex from "vuex";
// 引入store
import store from "./store";
// ...
Vue.use(Vuex); //使用vuex
引入会将这个引入代码执行一遍,将暴露的进行引入
执行了store/index.js就会创建store实例
那这么写呢?
import Vuex from "vuex";
// ...
Vue.use(Vuex); //使用vuex
// 引入store
import store from "./store";
不行,js会将所有import提升到头部执行
真正的解决方法:在store/index.js中创建实例前添加Vue.use(Vuex);
记得要引入Vue
此时完整的
// /src/store/index.js
// 该文件用于创建Vuex中最为核心的store
// 引入vuex
import Vuex from "vuex";
//引入Vue
import Vue from "vue";
// 准备actions 用于响应组件中的动作
const actions = {};
// 准备mutations 用于操作数据(状态)
const mutations = {};
// 准备state 用于存储数据
const state = {};
Vue.use(Vuex); //使用vuex
// 创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state,
});
// 暴露store
// export default store; //改为简写
至此完毕
案例
需求如下,先实现+按钮,其他同理
//Count.vue
// 加号按钮回调
increment() {
// this.sum += this.num; //原始写法
// 通过store调用dispatch 传入事件名称和参数
this.$store.dispatch("add", this.num);
},
store中也要准备好方法和数据
// /store/index.js
// 准备actions 用于响应组件中的动作
const actions = {
/**
* @param {*} context 上下文 简短版的store
* @param {*} value 传递的参数
*/
add(context, value) {
context.commit("ADD", value); //一般将mutations的方法全大写 进行区分
},
};
// 准备mutations 用于操作数据(状态)
const mutations = {
/**
* @param {*} state 存储数据的state
* @param {*} value 传递的参数
*/
ADD(state, value) {
state.sum += value;
},
};
// 准备state 用于存储数据
const state = {
sum: 0,
};
获取数据
<!--Count.vue-->
<h2>当前求和为:{{ $store.state.sum }}</h2>
其他是不是会了
再说两句
其中“当前求和为奇数时加”需求的业务逻辑可以写在actions中
addOdd(context, value) {
if (context.state.sum % 2) {
context.commit("ADD", value);
}
},
还有没有业务逻辑的如+可以直接调用this.$store.commit('ADD',this.num)
还有~ 如果actions的方法里需要处理的逻辑很多,可以再次调用context.dispatch()
触发另一个actions中的函数
还有~如果直接在actions函数中操作state数据,也能奏效但是开发者工具不认了捕获不到了
Vuex的开发者工具使用
因为vuex也是vue的开发团队所打造的所以直接使用之前的vue插件就可以
getters配置项
在创建store实例传入getters配置项
//....
// 准备getters 用于将state中的数据进行加工
const getters = {
formatSum(state) {
return state.sum * 10;
},
};
Vue.use(Vuex); //使用vuex
// 创建并暴露store
export default new Vuex.Store({
actions,
mutations,
state,
getters,
});
使用
<h4>当前求和放大10倍为:{{ $store.getters.formatSum }}</h4>
类似vm中data和computed关系
mapState与mapGetters
之前的案例,使用store的数据
<h2>当前求和为:{{ $store.state.sum }}</h2>
<h4>当前求和放大10倍为:{{ $store.getters.formatSum }}</h4>
结果没有问题,但是风格指南中说道,模板表达式应该尽量精简
所以能不能直接用{{sum}}
和{{formatSum}}
这里vuex就为我们提供了专属的方法
引入
import { mapState, mapGetters } from "vuex";
获取数据
借助mapState和mapGetters生成计算属性,从中读取数据
- 方式一:对象写法
先写到mounted函数中输出看看
参数中的key是希望使用时的属性名,value是store中定义的属性名
const x = mapState({ sum: "sum" });
const y = mapGetters({ formatSum: "formatSum" });
console.log(x);
console.log(y);
看控制台
是对象包裹的方法,方法返回的就是我们需要的数据了
将这些方法直接放到我们的computed中,那不就能直接取用了么
这里用了es6语法,将对象内容拆分出来放到另一个对象里
computed: {
...mapState({ sum: "sum" }),
...mapGetters({ formatSum: "formatSum" }),
},
- 方式二:数组写法
当取用的数据不用变换属性名,就可以使用这种简写形式
computed: {
...mapState(["sum" ]),
...mapGetters(["formatSum"]),
},
使用
就可以直接使用了
<h2>当前求和为:{{ sum }}</h2>
<h4>当前求和放大10倍为:{{ formatSum }}</h4>
注意
调试工具中,使用mapState或mapGetters生成的计算属性,并不会隶属于computed,而是区分出来属于vuexBindings
mapActions和mapMutations
类似上面的,就是方便调用actions和mutations中的方法
import { mapActions, mapMutations } from "vuex";
methods: {
...mapMutations({ increment: "ADD" }),
// 等同
// increment() {
// this.$store.commit("ADD", this.num);
// },
...mapActions({ incrementOdd: "addOdd" }),
// 等同
// incrementOdd() {
// this.$store.dispatch("add", this.num);
// },
}
使用时注意要传递参数
<button @click="increment(num)">+</button>
<button @click="incrementOdd(num)">当前求和为奇数时加</button>
同样,也有传递数组的写法
vuex模块化+namespace
如果我们继续按原来方法开发,最后actions或mutations中的方法会很多且杂乱
所以要分类
// store.js
const countOptions = {
namespaced: true, //默认false 为true后就可以通过mapState等获取到模块内数据
// 准备actions 用于响应组件中的动作
actions: {
//...
},
// 准备mutations 用于操作数据(状态)
mutations: {
//...
},
// 准备state 用于存储数据
state: {
//...
},
// 准备getters 用于将state中的数据进行加工
getters: {
//...
},
};
export default new Vuex.Store({
// actions,
// mutations,
// state,
// getters,
// 模块化编码
modules: {
countAbout: countOptions,
},
});
使用时,要在原始参数前加一个模块名称
...mapState("countAbout", { sum: "sum" }),
...mapMutations("countAbout", { increment: "ADD" }),
如果是直接用store调用,也要加上模块名
this.$store.state.countAbout.sum;
this.$store.getters["countAbout/formatSum"].sum;
this.$store.commit("countAbout/ADD", this.num)
需要注意的是state和getters的索引方式是不一样的
可见下图this.$store
输出
在优化就是将模块分到另一个js文件并暴露
在index.js中import
这样更精简