安装vuex
进入项目,在命令行中输入安装指令 npm install vuex --save
然后配置 vuex,使其工作起来:在src路径下创建store文件夹,然后创建index.js文件,文件内容如下:
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
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 Vue from 'vue';
import App from './App';
import router from './router';
import store from './store'; // 引入我们前面导出的store对象
Vue.config.productionTip = false;
new Vue({
el: '#app',
router,
store, // 把store对象添加到vue实例上
components: { App },
template: '<App/>',
});
最后修改App.vue:
<template>
<div></div>
</template>
<script>
export default {
mounted() {
// 使用this.$store.state.XXX可以直接访问到仓库中的状态
console.log(this.$store.state.name);
},
};
</script>
此时,启动项目npm run dev
,即可在控制台输出刚才我们定义在store中的name的值。
- 官方建议1: 官方建议我们以上操作this.$store.state.XXX最好放在计算属性中,这样可以让你的代码看起来更优雅一些,就像这样:
export default {
mounted() {
console.log(this.getName);
},
computed: {
getName() {
return this.$store.state.name;
},
},
};
此时可以得到和上面一样的效果。
- 官方建议2: 是不是每次都写this.$store.state.XXX让你感到厌烦,你实在不想写这个东西怎么办,当然有解决方案,就像下面这样:
<script>
import { mapState } from 'vuex'; // 从vuex中导入mapState
export default {
mounted() {
console.log(this.name);
},
computed: {
// 经过解构后,自动就添加到了计算属性中,此时就可以直接像访问计算属性一样访问它
...mapState(['name']),
},
};
</script>
你甚至可以在解构的时候给它赋别名,取外号,就像这样:
...mapState({ aliasName: 'name' }), // 赋别名的话,这里接收对象,而不是数组
了解修饰器:Getter
首先,在store对象中增加getters属性
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
name: '张三',
number: 0,
list: [
{ id: 1, name: '111' },
{ id: 2, name: '222' },
{ id: 3, name: '333' },
],
},
// 在store对象中增加getters属性
getters: {
getMessage(state) {
// 获取修饰后的name,第一个参数state为必要参数,必须写在形参上
return `hello${state.name}`;
},
},
});
export default store;
在组件中使用:
export default {
mounted() {
// 注意不是$store.state了,而是$store.getters
console.log(this.$store.state.name);
console.log(this.$store.getters.getMessage);
},
};
- 官方建议: this.$store.getters.XXX让可以使用mapGetters去解构到计算属性中,就像使用mapState一样,就可以直接使用this调用了,就像下面这样:
<script>
import { mapState, mapGetters } from 'vuex';
export default {
mounted() {
console.log(this.name);
console.log(this.getMessage);
},
computed: {
...mapState(['name']),
...mapGetters(['getMessage']),
},
};
</script>
此时可以得到和之前一样的效果。
当然,和mapState一样你也可以取别名,取外号,就像下面这样:
...mapGetters({ aliasName: 'getMessage' }), // 赋别名的话,这里接收对象,而不是数组
了解如何修改值:Mutation
// 错误示范
this.$store.state.XXX = XXX;
修改store/index.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
name: '张三',
number: 0,
},
mutations: {
// 增加nutations属性
setNumber(state) {
// 增加一个mutations的方法,方法的作用是让num从0变成5,state是必须默认参数
state.number = 5;
},
},
});
export default store;
修改App.vue
<script>
export default {
mounted() {
console.log(`旧值:${this.$store.state.number}`);
this.$store.commit('setNumber');
console.log(`新值:${this.$store.state.number}`);
},
};
</script>
以上是简单实现mutations的方法,是没有传参的,如果我们想传不固定的参数怎么办?修改store/index.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
name: '张三',
number: 0,
},
mutations: {
setNumber(state) {
state.number = 5;
},
setNumberIsWhat(state, number) {
// 增加一个带参数的mutations方法
state.number = number;
},
},
});
export default store;
修改App.vue
<script>
export default {
mounted() {
console.log(`旧值:${this.$store.state.number}`);
this.$store.commit('setNumberIsWhat', 666);
console.log(`新值:${this.$store.state.number}`);
},
};
</script>
- 注意:上面的这种传参的方式虽然可以达到目的,但是并不推荐,官方建议传递一个对象进去,这样看起来更美观,对象的名字你可以随意命名,但我们一般命名为payload,代码如下:
// store/index.js
mutations: {
setNumber(state) {
state.number = 5;
},
setNumberIsWhat(state, payload) {
// 增加一个带参数的mutations方法,并且官方建议payload为一个对象
state.number = payload.number;
},
},
修改App.vue
<script>
export default {
mounted() {
console.log(`旧值:${this.$store.state.number}`);
this.$store.commit('setNumberIsWhat', { number: 666 }); // 调用的时候也需要传递一个对象
console.log(`新值:${this.$store.state.number}`);
},
};
</script>
此时可以得到和之前一样的效果,并且代码更加美观!
- Mutations里面的函数必须是同步操作,不能包含异步操作!
- 官方建议:就像最开始的mapState和mapGetters一样,我们在组件中可以使用mapMutations以代替this.$store.commit('XXX'),是不是很方便呢?
<script>
import { mapMutations } from 'vuex';
export default {
mounted() {
this.setNumberIsWhat({ number: 999 });
},
methods: {
// 注意,mapMutations是解构到methods里面的,而不是计算属性了
...mapMutations(['setNumberIsWhat']),
},
};
</script>
此时可以得到和之前一样的效果,并且代码又美观了一点!
当然你也可以给它叫别名,取外号,就像这样:
methods: {
// 赋别名的话,这里接收对象,而不是数组
...mapMutations({ setNumberIsAlias: 'setNumberIsWhat' }),
},
了解异步操作:Actions
修改store/index.js
const store = new Vuex.Store({
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);
});
},
},
});
修改App.vue
async mounted() {
console.log(`旧值:${this.$store.state.number}`);
await this.$store.dispatch('setNum');
console.log(`新值:${this.$store.state.number}`);
},
- action就是去提交mutation的,什么异步操作都在action中消化了,最后再去提交mutation的。
当然,你可以模仿mutation进行传参,就像下面这样:修改store/index.js
actions: {
setNum(content, payload) {
// 增加payload参数
return new Promise(resolve => {
setTimeout(() => {
content.commit('setNumberIsWhat', { number: payload.number });
resolve();
}, 1000);
});
},
},
修改App.vue
async mounted() {
console.log(`旧值:${this.$store.state.number}`);
await this.$store.dispatch('setNum', { number: 611 });
console.log(`新值:${this.$store.state.number}`);
},
- 官方建议1:你如果不想一直使用this.$store.dispatch('XXX')这样的写法调用action,你可以采用mapActions的方式,把相关的actions解构到methods中,用this直接调用:
<script>
import { mapActions } from 'vuex';
export default {
methods: {
...mapActions(['setNum']), // 就像这样,解构到methods中
},
async mounted() {
await this.setNum({ number: 123 }); // 直接这样调用即可
},
};
</script>
当然,你也可以取别名,取外号,就像下面这样:
...mapActions({ setNumAlias: 'setNum' }), // 赋别名的话,这里接收对象,而不是数组
- 官方建议2:在store/index.js中的actions里面,方法的形参可以直接将commit解构出来,这样可以方便后续操作:
actions: {
setNum({ commit }) {
// 直接将content结构掉,解构出commit,下面就可以直接调用了
return new Promise(resolve => {
setTimeout(() => {
commit('XXXX'); // 直接调用
resolve();
}, 1000);
});
},
},
按属性进行拆分
目前我们介绍的store/index.js里面的内容是非常少的,如果你是一个稍微有些规格的项目,那么你将会得到一个成百上千行的index.js,然后查找一些东西就会非常费劲,我们建议你的一个文件内的行数尽量不要超过200行,不然对于调试来说没有好处。既然问题出来了,我们看一下怎么拆分一下。
一个store/index.js里面大致包含state/getters/mutations/actions这四个属性,我们可以彻底点,index.js里面就保持这个架子,把里面的内容四散到其他文件中。
于是我们可以这样拆分:
新建四个文件,分别是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 Vue from 'vue';
import Vuex from 'vuex';
import { state } from './state'; // 引入 state
import { getters } from './getters'; // 引入 getters
import { mutations } from './mutations'; // 引入 mutations
import { actions } from './actions'; // 引入 actions
Vue.use(Vuex);
const store = new Vuex.Store({
state: state,
getters: getters,
mutations: mutations,
actions: actions,
});
// 可以简写成下面这样:
// const store = new Vuex.Store({ state, getters, mutations, actions });
export default store;
以上这样就比较清晰了,大家各干各的,互不影响。
当然,你完全可以不这么做,引用官方文档中的一句话,“需要多人协作的大型项目中,这会很有帮助。但如果你不喜欢,你完全可以不这样做”。
按功能进行拆分 - Module模块化
上面我们介绍如何拆分项目,采用的是按属性的方式去拆分不同文件
接下来,我们介绍一下按另外的一个维度去拆分我们的store,‘按功能’,按功能拆分的话,就是我们的标题 Module(模块) 。
1. 我们在之前的store上,增加一个新的仓库store2,主要代码如下:
// store2.js
const store2 = {
state: {
name: '我是store2',
},
mutations: {},
getters: {},
actions: {},
};
export default store2;
2. 然后在store中引入我们新创建的store2模块:
import Vue from 'vue';
import Vuex from 'vuex';
import { state } from './state';
import { getters } from './getters';
import { mutations } from './mutations';
import { actions } from './actions';
import store2 from './store2'; // 引入store2模块
Vue.use(Vuex);
const store = new Vuex.Store({
modules: { store2 }, // 把store2模块挂载到store里面
state: state,
getters: getters,
mutations: mutations,
actions: actions,
});
export default store;
3. 访问state - 我们在App.vue测试访问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其实这里就是一种思想,将复杂的进行拆分,可以更有效的管理。
以上并不是module的全部,还有一些比如命名空间
、模块注册全局 action
、带命名空间的绑定函数
、模块动态注册
、模块重用
等方法这里就没介绍,如果你在项目中使用到了,再进行查阅即可
网友评论