安装开始
npm install vuex@next --save
创建 store.js
文件,且仅需提供一个初始state对象和mutation:
import { createStore } from 'vuex'
// 创建一个新的 store 实例
const store = createStore({
state: {
// 定义一个name,以供全局使用
name: '张三',
// 定义一个number,以供全局使用
number: 0,
// 定义一个list,以供全局使用
list: [
{ id: 1, name: '111' },
{ id: 2, name: '222' },
{ id: 3, name: '333' },
],
},
});
export default store
在main.js中导入
import { createApp } from 'vue';
import App from './App.vue';
//1.导入sotore
import store from './store.js';
const app = createApp(App);
//2.挂载
app.use(store);
app.mount('#app');
此时,vuex已经可以在你的项目中使用了。
<template>
<div></div>
</template>
<script>
export default {
mounted() {
// 使用this.$store.state.XXX可以直接访问到仓库中的状态
console.log(this.$store.state.name);
},
};
</script>
运行项目,可以在控制台看到我们在store中的name值。
vuex使用
接上文,官方建议,将 this.$store.state.xxx
放到计算属性中,所以我们可以这样写:
export default {
mounted() {
console.log(this.getName);
},
computed: {
getName() {
return this.$store.state.name;
},
},
};
但是,每次都写 this.$store.state.xxx
真的很烦,所以可以这么写:
import { mapState } from 'vuex'; // 从vuex中导入mapState
export default {
mounted() {
console.log(this.name);
},
computed: {
...mapState(['name']), // 经过解构后,自动就添加到了计算属性中,此时就可以直接像访问计算属性一样访问它
},
};
当然,在解构的时候也可以给他一个名字,方便使用:
...mapState({ aliasName: 'name' }), // 赋别名的话,这里接收对象,而不是数组
接下来我们更深入一点:先看vuex给的使用周期图:
图解:先找虚线框,虚线框里面的就是vuex,虚线框里面有一个state就是我们的数据,而要修改数据需要走如下的流程:
- 在组件里面调用Dispatch()方法提交Actions
- Actions再通过commit()方法提交Mutations
- 通过Mutations里面的方法改变state(数据)
- 响应(渲染)到组件里面
简单理解:
state:存数据的地方,所有的数据都要存在state里面。
Actions:与Mutations类似,包含的都是一些方法,不同的是Actions不能直接修改数据,它的作用是提交Mutations,Mutations里面的包含的才是具体操作数据的方法。
Mutations:唯一能修改数据的方法就是提交mutation,简单来说它里面存的就是一些操作数据的方法。
修饰器:Getter
修饰器,很简单理解就是修饰数据用的,比如,我们要在所有的name前面加上"Hello!",就可以用到修饰器了,首先,在store对象中增加getters属性:
getters: {
getMessage(state) {
// 获取修饰后的name,第一个参数state为必要参数,必须写在形参上
// state就是你的数据,不要多想
return `hello${state.name}`;
},
},
然后,在组件中使用:
mounted() {
// 注意不是$store.state了,而是$store.getters
console.log(this.$store.state.name);//张三
console.log(this.$store.getters.getMessage);//hello张三
},
如果你嫌这么写麻烦,官方又给了你建议:我们可以使用mapGetters去解构到计算属性中,就像使用mapState一样,就可以直接使用this调用了,就像下面这样:
import { mapState, mapGetters } from 'vuex';
export default {
mounted() {
console.log(this.name);
console.log(this.getMessage);
},
computed: {
...mapState(['name']),
...mapGetters(['getMessage']),
},
};
这么些得到的效果是和之前一样的,当然也可以取别名。
说到底,getters也是读取数据,只不过是修饰的。
修改值:Mutation
首先,必须说一下,store仓库里面的数据你可以随便拿,但是不能随便改,例如下面这种:
// 错误示范
this.$store.state.XXX = XXX;
如何修改数据,就要用到我们的Mutation,比如,我们先输出state中的number的值是0,然后我们在vue组件中提交mutations改变number的值为我们想要修改的值再输出:
在store.js中增加mutations如下:
mutations: {
// 增加nutations属性
setNumber(state) {
// 增加一个mutations的方法,方法的作用是让num从0变成5,state是必须默认参数
state.number = 5;
},
},
不要着急,这样并不会生效,你需要在组件中提交一下:
export default {
mounted() {
console.log(`旧值:${this.$store.state.number}`);//0
this.$store.commit('setNumber');//只有commit提交之后才会修改
console.log(`新值:${this.$store.state.number}`);//5
},
};
这是简单实现mutations的方法,没有传参,如果想传不固定的参数,请继续往下看:
mutations: {
setNumber(state) {
state.number = 5;
},
setNumberIsWhat(state, number) {
// 增加一个带参数的mutations方法
state.number = number;
},
},
然后在组件中提交的时候也要带上参数:
export default {
mounted() {
console.log(`旧值:${this.$store.state.number}`);//0
this.$store.commit('setNumberIsWhat', 666);
console.log(`新值:${this.$store.state.number}`);//666
},
};
注意:这么传参,我会经常写错,还是听官方的传递一个对象进去来:
mutations: {
setNumber(state) {
state.number = 5;
},
setNumberIsWhat(state, payload) {
// 增加一个带参数的mutations方法,并且官方建议payload为一个对象
state.number = payload.number;
},
},
组件中也需要修改一下:
export default {
mounted() {
console.log(`旧值:${this.$store.state.number}`);
this.$store.commit('setNumberIsWhat', { number: 666 }); // 调用的时候也需要传递一个对象
console.log(`新值:${this.$store.state.number}`);
},
};
这样,当你以后遇到复杂一点的传参是不是就会了!
注意:Mutations里面的函数必须是同步操作,不能包含异步操作!!!
但是,这种 this.$store.commit('xxx')
的写法是不是还是很难受,我们还是来用解构(store中的写法不变,看组件操作):
import { mapMutations } from 'vuex';
export default {
mounted() {
this.setNumberIsWhat({ number: 999 });
console.log(this.$store.state.count);//注意打印了一个对象包含999
},
methods: {
// 注意,mapMutations是解构到methods里面的,而不是计算属性了
...mapMutations(['setNumberIsWhat']),
},
};
上面特别提醒过,mutations只能进行同步操作,下面就说一下异步。
异步操作:Actions
Actions存在的意义是假设你在修改state的时候有异步操作,vuex作者不希望你将异步操作放在Mutations中,所以就给你设置了一个区域,让你放异步操作,这就是Actions.
const store = createStore({
state: {
name: '张三',
number: 0,
},
mutations: {
setNumberIsWhat(state, payload) {
state.number = payload.number;
},
},
actions: {
// 增加actions属性
setNum(content) {
// 增加setNum方法,默认第一个参数是content,其值是复制的一份store
return new Promise(resolve => {
// 我们模拟一个异步操作,1秒后修改number为888
setTimeout(() => {
content.commit('setNumberIsWhat', { number: 888 });
resolve();
}, 1000);
});
},
},
});
然后在组件中打印看看:
async mounted() {
console.log(`旧值:${this.$store.state.number}`);//0
await this.$store.dispatch('setNum');
console.log(`新值:${this.$store.state.number}`);//1秒后打印新值888
},
这就说明:action就是提交mutation的,所有的异步操作必须在action中进行操作,最后去提交给mutation。
在它里面也可以如mutation一样传参:
actions: {
setNum(content, payload) {
// 增加payload参数
return new Promise(resolve => {
setTimeout(() => {
content.commit('setNumberIsWhat', { number: payload.number });
resolve();
}, 1000);
});
},
},
然后在组件中传参:
async mounted() {
console.log(`旧值:${this.$store.state.number}`);//0
await this.$store.dispatch('setNum', { number: 666 });
console.log(`新值:${this.$store.state.number}`);//666
},
如果你不想使用 this.$store.dispatch('XXX')
这样的写法去调用action,你可以采用mapActions的方法,把它解构到methos中,然后用this直接调用:
import { mapActions } from 'vuex';
export default {
methods: {
...mapActions(['setNum']), // 就像这样,解构到methods中
},
async mounted() {
await this.setNum({ number: 123 }); // 直接这样调用即可
},
};
官方建议:在actions中,方法的形参可以直接将commit解构出来,方便操作:
actions: {
setNum({ commit }) {
// 直接将content解构掉,解构出commit,下面就可以直接调用了
return new Promise(resolve => {
setTimeout(() => {
commit('XXXX'); // 直接调用提交
//免去这一步:content.commit('setNumberIsWhat', { number: payload.number });
resolve();
}, 1000);
});
},
},
action存在的本质就是处理异步,它其实就是mutation的上一级,在action这里处理完异步的一些操作后,修改state就交给mutation去做了。
vuex拆分
当我们的项目稍微有规格的话,都放在一个store.js文件中是不是会有成百上千行,然后你查找一些东西会很费解,当然这可能说的是你,我目前还没这水平,但是提前研究一下怎么拆分。
按属性拆分
一个store里面基本包含四个属性:
那我们就按照属性来拆分:
新建四个文件,分别是 state.js
getters.js
mutations.js
actions.js
1.拆出来state放到state.js中:
// state.js
export const state = {
name: '张三',
number: 0,
list: [
{ id: 1, name: '111' },
{ id: 2, name: '222' },
{ id: 3, name: '333' },
],
};
2.拆出来 getters
放到 getters.js
中:
// getters.js
export const getters = {
getMessage(state) {
return `hello${state.name}`;
},
};
3.拆出来 mutations
放到 mutations.js
中:
// mutations.js
export const mutations = {
setNumber(state) {
state.number = 5;
},
};
4.拆出来 actions
放到 actions.js
文件中:
// actions.js
export const actions = {
setNum(content) {
return new Promise(resolve => {
setTimeout(() => {
content.commit('setNumberIsWhat', { number: 888 });
resolve();
}, 1000);
});
},
};
5.组装到主文件里面
import { state } from './state'; // 引入 state
import { getters } from './getters'; // 引入 getters
import { mutations } from './mutations'; // 引入 mutations
import { actions } from './actions'; // 引入 actions
import { createStore } from 'vuex'
const store = createStore({
state: state,
getters: getters,
mutations: mutations,
actions: actions,
})
export default store;
这个按属性拆分其实呃,就是比较清晰了。
按功能拆分
接下来我们以另外一个维度去拆分store,按功能拆分,就是Module(模块)。
读一下官方文档,你可能就明白了,我们有一个总的store,再根据不同的功能,添加两个不同的store,每个store里面维护不同的state,及自己的actions/mutations/getters。
当我们有一个store.js之后,我们再增加一个新的store2.js:
// store2.js
const store2 = {
state: {
name: '我是store2',
},
mutations: {},
getters: {},
actions: {},
};
export default store2;
然后在store中引入我们新加的store2模块:
import { state } from './state'; // 引入 state
import { getters } from './getters'; // 引入 getters
import { mutations } from './mutations'; // 引入 mutations
import { actions } from './actions'; // 引入 actions
import store2 from './store2'; // 引入store2模块
import { createStore } from 'vuex'
const store = createStore({
state: state,
getters: getters,
mutations: mutations,
actions: actions,
})
export default store;
访问state,我们在组件中访问store2模块的state中的name,需要这么写:
<template>
<div></div>
</template>
<script>
export default {
mounted() {
console.log(this.$store.state.store2.name); // 访问store2里面的name属性
},
};
</script>
我们通过下面的代码可以了解到在不同的属性里是怎么访问 模块内的状态 或者 根状态:
mutations: {
changeName(state, payload) {
// state 局部状态
console.log(state);
console.log(payload.where);
},
},
getters: {
testGetters(state, getters, rootState) {
// state 局部状态
console.log(state);
// 局部 getters,
console.log(getters);
// rootState 根节点状态
console.log(rootState);
},
},
actions: {
increment({ state, commit, rootState }) {
// state 局部状态
console.log(state);
// rootState 根节点状态
console.log(rootState);
},
},
以上是对module的简单介绍,其实这里就是一种思想,分而治之,将复杂的进行拆分,可以更有效的管理。
更多相关信息,请查阅官方文档:https://vuex.vuejs.org/zh/guide/modules.html
本文内容借鉴自:
作者:三年没洗澡
链接:https://juejin.cn/post/6928468842377117709
来源:稀土掘金