一、vuex是什么
vue官方提供的状态管理器。用于处理全局的数据共享问题的。
二、vuex的仓库中的几个核心概念
- state
vuex中的数据源,专门存共享数据的地方。 - getter
可以针对现有的 state 数据或者其余的 getter 做的一个二次计算,可以理解为仓库的计算属性。 - mutation
唯一能够修改 state 数据的东西,不允许写异步代码。 - action
它里面可以写异步代码,它如果要修改 state 数据,是通过去调用 mutation。 - module
仓库模块的拆分,当项目足够大的时候应该把vuex分成多个子模块。
仓库子模块其实就是一个包含有 state 、getters、mutations、actions 的对象
使用vuex
- 项目安装vuex
npm install vuex
- 在 src 目录下创建一个store的文件夹并在里面创建一个index.js文件和modules的文件夹,这样可以较好的区分子模块。如图:
在index.js里实例化 Vuex 的一个 store仓库实例对象并暴露出去。
仓库根模块
// 1. 引入 Vue
import Vue from "vue";
// 2. 引入 Vuex
import Vuex from "vuex";
// 引入拆分出来的仓库子模块()
//仓库子模块其实就是一个包含有 state 、getters、mutations、actions 的对象
import moduleA from "./modules/a模块";
import moduleB from "./modules/b模块";
// 3. 调用
Vue.use(Vuex);
// 4. 生成 仓库 实例对象
const store = new Vuex.Store({
// 配置 modules
modules: {
a: moduleA,
b: moduleB,
},
});
// end. 暴露 store
export default store;
仓库子模块 module
- 仓库拆分子模块之后,没有设置命名空间。有一些问题的
- 多个仓库子模块中的 getter 不能同名,否则会报错
- 多个仓库子模块中的 mutation 如果同名的化,组件调用这个 mutation 时。都会被触发, 会有污染的风险
- 多个仓库子模块中的 action 如果同名的化,组件调用这个 action 时。都会被触发, 会有污染的风险
- 由于有上面的这种问题存在,所以推荐仓库子模块都设置上命名空间。
给仓库子模块的 那个对象配置一个 namespaced 属性。属性值为 true
// 拆分的仓库子模块A
const moduleA = {
namespaced: true, //仓库子模块的命名空间
state: {
name: '张三'
},
mutations: {
//mutations会自动接收 state, payload是传过来的值
SET_NAME(state, payload) {
state.name = payload
}
},
actions: {
//actions会自动接收 content, payload是传过来的值
SYNC_SET_NAME(content, payload){
setTimeout(() => {
// 调用 SET_NAME 这个 mutation 来修改 state 数据
context.commit("SET_NAME", payload);
}, 2000);
}
},
getters: {
firstName(state, pa) {
//getters会自动接收 state
return state.name.substr(0,1);
}
},
// 仓库子模块也可以继续去做拆分,但是没必要搞这么复杂
modules: { ... }
}
export default moduleA;
- 需要在 src目录下的main.js 文件的 new Vue() 的位置去配置 store 选项,选项值是上一个步骤中暴露出来的 store 的实例。
import Vue from "vue";
import App from "./App.vue";
import store from "./store";//index可以省略
Vue.config.productionTip = false;//这个是阻止启动生产消息设不设置看你喜欢
new Vue({
store,
render: (h) => h(App),
}).$mount("#app");
三、使用 state 与 getter
一、使用挂载到 Vue原型上的 $store 对象。这个 $store 就是 new Vuex.Store() 生成的仓库实例对象
//在组件里用 (建议在组件的 computed 里用)
this.$store.state['a/name'] //moduleA的state a代表的是moduleA的命名
this.$store.getters['a/firstName'] //moduleA的getters a代表的是moduleA的命名
//用在组件上
<h1>{{ $store.state['a/name'] }}</h1> 输出结果为张三
<h1>{{ $store.state['a/firstName'] }}</h1> 输出结果为张
二、使用 vuex 提供的辅助函数
// 引入 辅助函数 。采用类似解构赋值的方式
import { mapState, mapGetters } from 'vuex'
//在组件的 computed 里用
computed: {
//辅助函数返回值是一个对象,需要用扩展运算符展开
//辅助函数接收一个数组做为参数,参数中的每一项就是该辅助函数对应在仓库中的值 有命名空间的仓库模块要在第一个参数里写上命名
...mapState('a', ['name']), a是moduleA的命名 name是要用的state里的数据
...mapGetters('a', ['firstName'])
}
//用在组件上
<h1>{{ name }}</h1>输出结果为张三
<h1>{{ firstName }}</h1>输出结果为张
}
四、修改 state 与 getter 数据
首先明确一点。state 可以修改。getter 不能修改。
1. 仓库中要提供 对应 state 修改的 mutation 函数
2. 在组件中去调用 mutation
一、在组件中先定义一个函数,使用 vuex 绑定到 vue 原型上的 $store 这个对象的 commit() 方法
methods: {
SET_NAME(name) {
this.$store.commit('a/SET_NAME', name);
}
}
//用在组件上
<h1>{{ $store.state['a/name'] }}</h1> 点击后输出结果为李四
<h1>{{ $store.state['a/firstName'] }}</h1> 点击后输出结果为李
<button @click="SET_NAME('李四')">修改name</button>
二、使用 mapMutations 辅助函数
// 引入 辅助函数 。采用类似解构赋值的方式
import { mapState, mapGetters, mapMutations } from 'vuex'
computed: {
...mapState('a', ['name']), a是moduleA的命名 name是要用的state里的数据
...mapGetters('a', ['firstName'])
}
methods: {
...mapMutations('a', ['SET_NAME']) a是moduleA的命名 SET_NAME是mutations要用的里的函数
}
//用在组件上
<h1>{{ name }}</h1> 点击后输出结果为李四
<h1>{{ firstName }}</h1> 点击后输出结果为李
<button @click="SET_NAME('李四')">修改name</button>
五、异步修改 state
首先需要知道,mutation 里面只允许同步的去修改 state 数据。(虽然在mutation中可以异步的去修改state数据不会报错,但是会导致时间旅行等机制没有效果)
如果异步的修改的话,有两个大方案
- 不涉及action。在组件上异步代码走完之后再去调用 mutation
- 使用 action
- action 类似于 mutation,不同在于:
- action 提交的是 mutation,而不是直接变更状态。
- action 可以包含任意异步操作。
使用 action 异步的修改 state 数据
// 步骤和上面差不多
// 1. 通过 $store 上的 dispatch 方法
this.$store.dispatch('ma/SYNC_SET_NAME', payload)
// 2.mapActions辅助函数
methods: {
...mapActions('a', ['SYNC_SET_NAME'])
}
结尾、建议使用 vueDevtools 来辅助开发,如图:
用 vueDevtools 就可以观察到仓库中的数据