文章目录
一、简介
我们来看看对 Vuex 比较专业的介绍:
Vuex 是一个专为 Vue 开发的应用程序的状态管理模式,它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
简而言之,Vuex 采用类似全局对象的形式来管理所有组件的公用数据,如果想修改这个全局对象的数据,得按照Vuex提供的方式来修改(不能自己随意用自己的方式来修改)。
二、优点
Vuex状态管理跟使用传统全局变量的不同之处:
-
Vuex的状态存储是响应式的: 就是当你的组件使用到了这个 Vuex 的状态,一旦它改变了,所有关联的组件都会自动更新相对应的数据,这样开发者省事很多。
-
不能直接修改Vuex的状态: 如果是个全局对象变量,要修改很容易,但是在 Vuex 中不能这样做,想修改就得使用 Vuex 提供的唯一途径:显示地提交(
commint
)mutations
来实现修改。这样做的好处就是方便我们跟踪每一个状态的变化,在开发过程中调试的时候,非常实用。
三、使用步骤
1. 安装Vuex
npm install vuex --save
2. 引用Vuex
在App.vue文件中:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
3. 创建仓库Store
要使用 Vuex,我们要创建一个实例 store
,我们称之为仓库,利用这个仓库 store
来对我们的状态进行管理。
//创建一个 store
const store = new Vuex.Store({});
四、包含模块
- State:定义了应用状态的数据结构,可以在这里设置默认的初始状态。
- Getter:允许组件从 store 中获取数据,
mapGetters
辅助函数仅仅是将 store 中的getter
映射到局部计算属性。 - Mutation:是唯一更改 store 中状态的方法,而且要是
同步函数
。 - Action:用于提交
mutation
,而不是直接变更状态,可以包含任意异步操作。 - Module:可以将 store 分割成模块(module)。每个模块拥有自己的
state
、mutation
、action
、getter
、甚至是嵌套子模块
Vuex的作用类似全局对象,Vuex 使用单一状态树,用一个对象State包含了整个应用层级的所有状态,你可以理解为这些状态就是一堆全局变量和数据。
1. State
假设我们有一个全局状态 count
的值为 5。那么,我们就可以将其定义为 state
对象中的 key
和 value
,作为全局状态供我们使用。如下:
-
Vue2.x中:
//创建一个 store const store = new Vuex.Store({ //state存储应用层的状态 state:{ count:5 //总数:5 } });
-
Vue3.x中:
import { createStore } from 'vuex' export default createStore({ state: { count:5 //总数:5 } }
2. Getters
可以认为,getters
是store的计算属性,类似于computed
,对state里的数据进行一些过滤,改造等等
假设我们要在state.count
的基础上派生出一个新的状态newCount
出来,就适合使用我们的 getters
getters
接受 state
作为其第一个参数
-
Vue2.x中:
const store = new Vuex.Store({ //state存储应用层的状态 state:{ count:5 //总数:5 }, getters:{ newCount:state => state.count * 3 } });
在组件中获取
{{newCount}}
方式:export default { computed: { newCount(){ return this.$store.getters.newCount; } } };
-
Vue3.x中:
import { createStore } from 'vuex' export default createStore({ state: { count:5 //总数:5 }, getters: { newCount:state => state.count * 3 }, }
在组件中获取
{{newCount}}
方式:<template> <span>{{ $store.getters.newCount }}</span> </<template> <script setup> import { useStore } from 'vuex'; const store = useStore(); const getNewCount= () => { console.log(store.getters.newCount); }; </script>
3. Mutations
Vuex
给我们提供修改仓库 store
中的状态的唯一办法就是通过提交mutation
mutation必须是同步函数
,这是 Vuex 设计时的一个核心原则:
- Vuex 的状态跟踪机制依赖于同步的 mutations
- 当 mutation 异步执行时,Vuex 无法准确地知道何时状态已经更新,这可能导致无法正确地显示状态变化
在 mutations 中,可以直接修改state状态
-
Vue2.x中:
const store = new Vuex.Store({ //state存储应用层的状态 state:{ count:5 //总数:5 }, // mutations是修改state中数据的唯一途径 mutations:{ increment(state,value){ state.count += value; } } });
-
Vue3.x中:
import { createStore } from 'vuex' export default createStore({ state: { count:5 //总数:5 }, mutations:{ increment(state,value){ state.count += value; } } }
通过 commit 方法被调用,通常是在组件中通过 this.$store.commit("increment", 666);
的方式触发
我们在提交commit
时候,第一个参数"increment"
,就是对应在 mutations
中的increment
方法,第二个参数是自定义值。例如:
-
Vue2.x中:
methods: { getVal(event) { //通过commit提交一个名为increment的mutation this.$store.commit("increment", 666); } }
-
Vue3.x中:
import { useStore } from 'vuex'; const store = useStore(); const getVal = event => { //通过commit提交一个名为increment的mutation store.commit("increment", 666); }
4. Action
- Actions 是可以包含任意异步操作的函数
- 在 actions 中,不能直接修改状态(state),而需要通过 dispatch 方法提交一个 mutation 来更改状态
- 只有通过
action => mutations => states
,这个流程进行操作,具体步骤如下:
export default new Vuex.Store({
//存放数据
state: {
obj: {},
},
//4. 通过commit mutations中的方法来处理
mutations: {
getParam(state, value) {
//5.修改state中的数据
state.obj = value
}
},
//2.接受dispatch传递过来的方法和参数
actions: {
getParamSync(store, object) {
// 异步操作(例如网络请求)
axios.post('https://api.example.com/submit',{data: object}).then(function (response) {
const value = response.data; // 从服务器获取的数据
//3.通过commit提交一个名为getParam的mutation
//action 函数接收一个 store 的实例对象,因此你可以调用 store.commit 提交一个 mutation
store.commit('getParam', value);
})
}
}
})
通过 dispatch 方法被调用,通常是在组件中通过 this.$store.dispatch('getParamSync',{name,age,sex})
的方式触发
methods: {
getVal() {
let name= 'xia';
let age= '26';
let sex= 'man';
//1.通过dispatch将方法getParamSync和多个参数{name,age,sex}传递给actions
this.$store.dispatch('getParamSync',{name,age,sex})
}
}
5. Modules
随着项目的复杂度增大,为了方便管理 Vuex,一般会将其按功能分割成不同的模块(Module
),方便日后管理。每个模块拥有自己的 state
、mutation
、action
、getter
甚至是嵌套子模块
import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'
import mutations from './mutations'
import actions from './actions'
import * as getters from './getters'
import moduleA from './module/moduleA' // 模块A
import moduleB from './module/moduleB' // 模块B
Vue.use(Vuex)
export default new Vuex.Store({
actions,
getters,
state,
mutations,
modules: {
moduleA,
moduleB
}
})
moduleA.js
/ moduleB.js
文件
// 每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块
export default {
state: {
text: 'moduleA'
},
getters: {},
mutations: {},
actions: {}
}
然后我们就在组件里这么调用就可以了
<template>
<div class="demo">
<h1>{{getText1}}</h1>
<h1>{{getText2}}</h1>
</div>
</template>
computed: {
getText1(){
return this.$store.state.moduleA.text;
},
//或
...mapState({
getText2: state => state.moduleB.text;
})
}
由此可知,模块内部的 state 是局部的,只属于模块本身所有,所以外部必须通过对应的模块名进行访问。
五、Vuex最最简单的项目实例
1. store文件目录结构
index.js
import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'
import mutations from './mutations'
import actions from './actions'
import * as getters from './getters'
//每次修改state都会在控制台打印log
import createLogger from 'vuex/dist/logger'
Vue.use(Vuex)
const debug = process.env.NODE_ENV !== 'production'
export default new Vuex.Store({
actions,
getters,
state,
mutations,
strict: debug, // 当debug=true时开启严格模式(性能有损耗)
plugins: debug ? [createLogger()] : []
})
state.js
const state = {
news: {}
}
export default state;
mutations.js
const mutations = {
SET_NEWS(state, val) {
state.news= val
}
}
export default mutations;
actions.js
//异步处理
const actions = {
M_NEWS({ commit }, object) {
// 异步操作(例如网络请求)
axios.post('https://api.example.com/submit',{data: object}).then(function (response) {
const value = response.data; // 从服务器获取的数据
commit('SET_NEWS', value); // commit mutations修改
})
}
}
export default actions;
getters.js
// 通常通过getters取数据 (this.$store.getters.news;)
export const news = state => state.news // 不做其他处理 直接映射出去
2. 使用store
在 main.js
中引用
import store from './store' //vuex存储文件
new Vue({
el: '#app',
router,
store,
components: {
App
},
template: '<App/>'
})
3. 操作数据
运用vuex语法糖 mapState
、mapGetters
和 mapMutations
-
mapState
:映射 state 到组件的计算属性:...mapState(["news"])
就相当于this.$store.state.news
-
mapGetters
:映射 getters 到组件的计算属性:...mapGetters(["news"])
就相当于this.$store.getters.news
-
mapMutations
:映射 mutations 到 组件的 methods:...mapMutations([{ changeNews: "SET_NEWS" }])
就相当于给 SET_NEWS 重命名为 changeNews
this.changeNews(val)
就相当于this.$store.commit("SET_NEWS", val)
<template>
<div>
<p>News: {{ news }}</p>
<button @click="submit">ChangeNews</button>
</div>
</template>
<script>
import { mapState, mapGetters, mapMutations } from "vuex";
export default {
computed: {
// 映射 state 到组件的计算属性
...mapState(["news"]) // 相当于 this.$store.state.news (vuex语法糖)
// 或者
// 映射 getters 到组件的计算属性
...mapGetters(["news"]) // 相当于 this.$store.getters.news (vuex语法糖)
},
methods: {
...mapMutations([{
// 将 changeNews 与 mutations中的 SET_NEWS 关联
changeNews: "SET_NEWS" // 相当于给 SET_NEWS 重命名为 changeNews
}]),
submit () {
let val = 'test news';
this.changeNews(val); // 相当于 this.$store.commit("SET_NEWS", val);
}
}
}
</script>
六、Vue3 setup 中使用
在 Vue2 项目中可以使用 this.$store
获取 vuex 里的数据和保存全局数据,但是在 Vue3 的 setup 函数里,并没有 this
这个概念,这里可以使用 useStore
代替,具体使用如下:
<template>
<div>
<p>News: {{ news }}</p>
<button @click="changeNews">Change News</button>
</div>
</template>
<script>
import { ref, onMounted } from 'vue';
import { useStore } from 'vuex';
export default {
setup() {
// 使用 useStore 钩子获取对 Vuex store 的引用
const store = useStore();
// 创建一个响应式引用来存储 news
const news = ref(store.state.news);
// 定义一个方法来改变 news
const changeNews = () => {
// 提交 mutation 来改变 news
store.commit('SET_NEWS', 'test news');
};
// 返回需要在模板中使用的响应式引用和方法
return {
news,
changeNews,
};
},
};
</script>
store/index.js
:
// Vue2.x 中创建 store
// const store = new Vuex.Store({
// state:{
// count:5 //总数:5
// }
// });
// Vue3.x 中通过 createStore 创建 store
import { createStore } from 'vuex'
export default createStore({
state: {
news: null
},
mutations: {
SET_NEWS: (state, value) => {
state.news = value;
}
},
getters: {
news : state => { state.news || sessionStorage.news }
},
actions: {
getData(store) {
// 模拟异步
setTimeout(() => {
store.commit('SET_NEWS', '');
}, 2000);
}
},
modules: {}
})