也许有人会说。vuex已经被淘汰了,没人用,现在都是用大菠萝(pinia),但我得想法是,学一个东西要学习他的背景,为什么出现,然后一步一步的进行优化,才有今天的产物Pinia,由于现在有vue2和vue3,在这两个里面的用法可能不太一样,人非圣贤,孰能无过,希望我在错误的时候,大家指正一下,废话不多说,开始学习vuex。以下都来自官方文档!
什么是“状态管理模式”?
让我们从一个简单的 Vue 计数应用开始:
new Vue({
// state
data () {
return {
count: 0
}
},
// view
template: `
<div>{{ count }}</div>
`,
// actions
methods: {
increment () {
this.count++
}
}
})
这个状态自管理应用包含以下几个部分:
- state,驱动应用的数据源;
- view,以声明方式将 state 映射到视图;
- actions,响应在 view 上的用户输入导致的状态变化。
以下是一个表示“单向数据流”理念的简单示意:
但是,当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏:
- 多个视图依赖于同一状态。
- 来自不同视图的行为需要变更同一状态。
对于问题一,传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。对于问题二,我们经常会采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码。
因此,我们为什么不把组件的共享状态抽取出来,以一个全局单例模式管理呢?在这种模式下,我们的组件树构成了一个巨大的“视图”,不管在树的哪个位置,任何组件都能获取状态或者触发行为!
通过定义和隔离状态管理中的各种概念并通过强制规则维持视图和状态间的独立性,我们的代码将会变得更结构化且易维护。
这就是 Vuex 背后的基本思想
什么情况下我应该使用 Vuex?
Vuex 可以帮助我们管理共享状态,并附带了更多的概念和框架。这需要对短期和长期效益进行权衡。
如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您最好不要使用 Vuex。一个简单的 store 模式 (opens new window)就足够您所需了。但是,如果您需要构建一个中大型单页应用,您很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择。
为什么要学习vuex?
我曾经听过王老师一句话,任何一个新技术的出现,都是为了解决以前的痛点。
我们知道,数据传递的方式有很多,父传子,子发射emit事件,还有兄弟相互传,但如果嵌套很多层级呢,一个一个往下传,即使你会写,难免麻烦,vuex就是解决这个痛点,可以理解vuex是一个仓库,存放一些数据,某个组件想用仓库里面的东西可以直接拿取
使用vuex
步骤一:NPM:
使用 npm 包管理器安装 Vuex ,--save是全局安装的意思
npm install vuex --save
在Main.js文件中使用Vue.use(Vuex)来显式地安装Vuex插件。在整个应用程序中使用Vuex的功能。
import { createApp } from 'vue';
import App from './App.vue';
import store from './store'; // 引入刚刚创建的 store
const app = createApp(App);
app.use(store); // 使用 store
app.mount('#app');
步骤二:创建仓库
在src文件下创建一个名为store的文件夹,在文件夹里面创建Index.js文件
import { createStore } from "vuex";
// 创建 Vuex store
const store = createStore({
// state 中存放全局共享的数据
state() {
return {
counter: 0, // 初始化 counter 的值
};
},
// 可以在这里添加 mutations、actions 和 getters
mutations: {},
actions: {},
getters: {},
});
// 导出 store
export default store;
说明:
- state: 存放全局共享的数据,这里初始化了
counter
。 - mutations: 用于修改 state 的方法。
- actions: 用于处理异步操作。
- getters: 用于获取 state 的数据并进行一些计算。
步骤三:state的使用(一下的方法会一种就行)
在template模板上访问
方法一:
vue2 和vue3在模板上都可以这么访问
$store.state.counter
<template>
<div>
<p>Counter: {{ $store.state.counter }}</p>
</div>
</template>
方法二:
vue2中computed
在vue2中,有两种写法
export default {
computed: {
counter() {
return this.$store.state.counter;
}
},
// 其他组件选项...
}
<p>Counter: {{ counter }}</p>
vue2中 mapState映射
- 导入mapState并把所需要的State域中的属性映射为当前组件的计算属性。
import { mapState } from 'vuex';
export default {
computed: {
...mapState(['counter']) // 将 counter 映射为组件的计算属性
},
// 其他组件选项...
}
<p>Counter: {{ counter }}</p>
vue3中
第一种computed
<script setup>
import { computed } from 'vue';
import { useStore } from 'vuex';
const store = useStore();
const counter = computed(() => store.state.counter);
</script>
第二种useStore
<script setup>
import {toRefs} from "vue"
import { useStore } from 'vuex';
const store = useStore();
const {name, level} = toRefs(store.state)
</script>
步骤四:getter的使用
getter相当于computed,
import { createStore } from "vuex";
// 创建 Vuex store
const store = createStore({
// state 中存放全局共享的数据
state() {
return {
counter: 0, // 初始化 counter 的值
users:[{id:1,name:'why',age:20},{id:2,name:'jion',age:30}]
};
},
// 可以在这里添加 mutations、actions 和 getters
mutations: {},
actions: {},
getters: {
doubleCounter(state) {return state.counter*2},
totalAge(state) {
return state.users.reduce((preValue,item) =>{
return preValue+item.age
},0)
}
},
});
// 导出 store
export default store;
如果一个Getter里面想拿另一个getter,那么需要在getter下的函数传递两个参数,第一个参数是state,第二个参数就是getter,
import { createStore } from "vuex";
// 创建 Vuex store
const store = createStore({
// state 中存放全局共享的数据
state() {
return {
counter: 0, // 初始化 counter 的值
users:[{id:1,name:'why',age:20},{id:2,name:'jion',age:30}]
};
},
// 可以在这里添加 mutations、actions 和 getters
mutations: {},
actions: {},
getters: {
doubleCounter(state) {return state.counter*2},
totalAge(state) {
return state.users.reduce((preValue,item) =>{
return preValue+item.age
},0)
},
doubleGetter(state,getter) {return `total:${getter.doubleCounter}`},
},
});
// 导出 store
export default store;
getter还可以封装函数,例如我想获取某一个id的所有信息
import { createStore } from "vuex";
// 创建 Vuex store
const store = createStore({
// state 中存放全局共享的数据
state() {
return {
counter: 0, // 初始化 counter 的值
users:[{id:1,name:'why',age:20},{id:2,name:'jion',age:30}]
};
},
getters: {
getFriend(state){
return function(id) {
const friend = state.users.find(item => item.id == id)
return friend
}
}
},
});
// 导出 store
export default store;
<p>Counter: {{ $store.getter.getFriend(1)}}</p>
getter映射:mapGetters
在vue2中
第一种
import { mapState } from 'vuex';
export default {
computed: {
...mapGetter(['doubleCounter'])
},
// 其他组件选项...
}
第二种
在vue3中
<script setup>
import { computed } from 'vue';
import { useStore } from 'vuex';
const store = useStore();
const message = computed(()=>{store.getter.doubleCounter})
</script>
步骤五:Mutations的使用
更改vuex中的store唯一的办法就是提交mutations
import { createStore } from "vuex";
// 创建 Vuex store
const store = createStore({
// state 中存放全局共享的数据
state() {
return {
counter: 0, // 初始化 counter 的值
};
},
// 可以在这里添加 mutations、actions 和 getters
mutations: {changeCounter(state){state.counter = 1}},
actions: {},
getters: {},
});
// 导出 store
export default store;
export default {
methods: {
//点击事件,提交changeCounter
changeCounter(){
this.$store.commit('changeCounter')
}
},
// 其他组件选项...
}
如果想让外部更改数据的话,传入两个参数,提交的时候传进来一个参数或者一个对象
import { createStore } from "vuex";
// 创建 Vuex store
const store = createStore({
// state 中存放全局共享的数据
state() {
return {
counter: 0, // 初始化 counter 的值
};
},
// 可以在这里添加 mutations、actions 和 getters
mutations: {changeCounter(state,payload){state.counter = payload}},
actions: {},
getters: {},
});
// 导出 store
export default store;
export default {
methods: {
//点击事件,提交changeCounter
changeCounter(){
this.$store.commit('changeCounter',222)
}
},
// 其他组件选项...
}
常量使用
在store文件夹下建立一个mutation_type.js文件
export const CHANGE_INFO = 'CHANGE_INFO'
import { createStore } from "vuex";
//引入这个常量
import { CHANGE_INFO } from "./mutation/type"
// 创建 Vuex store
const store = createStore({
// state 中存放全局共享的数据
state() {
return {
counter: 0, // 初始化 counter 的值
};
},
// 可以在这里添加 mutations、actions 和 getters
mutations: {[CHANGE_INFO ](state){state.counter = 1}},
actions: {},
getters: {},
});
// 导出 store
export default store;
这里的中括号的意思就是
const key = 123
const obj = {
[key]:345
}
在模板里面
//引入常量
import {CHANGE_INFO} from "@/store/mutation/type.js"
export default {
methods: {
//点击事件,提交changeCounter
changeCounter(){
this.$store.commit(CHANGE_INFO,222)
}
},
// 其他组件选项...
}
mapMutations映射
import { mapMutations} from 'vuex';
export default {
computed: {
...mapMutations(['changeCounter'])
},
// 其他组件选项...
}
重要原则
Mutations内部不要执行异步操作,只能是同步
步骤六:actions的使用发送网络请求,
不允许直接拿state,还是要进行提交mutations 来进行修改,在这里可以执行异步操作,
import { createStore } from "vuex";
// 创建 Vuex store
const store = createStore({
// state 中存放全局共享的数据
state() {
return {
counter: 0, // 初始化 counter 的值
};
},
// 可以在这里添加 mutations、actions 和 getters
mutations: {changeCounter(state,payload){state.counter = payload}},
actions: {
incrementAction(context){ console.log(context.commit,context.getter,context.state)
context.commit('changeCounter')
}
},
getters: {},
});
// 导出 store
export default store;
import { mapMutations} from 'vuex';
export default {
methods: {
//点击事件
actionRtnClick(){ this.$store.dispatch('incrementAction')}
},
// 其他组件选项...
}
action传参
import { createStore } from "vuex";
// 创建 Vuex store
const store = createStore({
// state 中存放全局共享的数据
state() {
return {
counter: 0, // 初始化 counter 的值
};
},
// 可以在这里添加 mutations、actions 和 getters
mutations: {changeCounter(state,payload){state.counter = payload}},
actions: {
changeAction(context,payload){ console.log(context.commit,context.getter,(context.state)
console.log(payload)
context.commit('changeCounter',payload)
}
},
getters: {},
});
// 导出 store
export default store;
import { mapMutations} from 'vuex';
export default {
methods: {
//点击事件
actionRtnClick(){ this.$store.dispatch('changeAction',333)}
},
// 其他组件选项...
}
mapAction映射
import { mapMutations} from 'vuex';
export default {
computed: {
...mapActions(['changeAction'])
},
// 其他组件选项...
}
在模板上就可以直接绑定这个方法
<p @click="changeActions(777)"></p>
发送网络请求
会前面的网络请求就很简单,思路就是在action里面进行网络请求,模板在页面未加载的钩子函数里面就发起派发一个dispatch这个actions里面的函数,得到res,在通过mutatiom修改state,页面引入vuex就可以使用了。
步骤七:modules的使用
最后一个知识点
因为一个项目可能有很多个组件,现在就是所有组件的数据都在一个state里面,vuex是一个单一数据源,不建议多个store,modules就是给store分模块
在store文件夹下建立一个modules文件夹,里面放入home.js文件,
const counter = {
state() {
return {
counter: [], // 初始化 counter 的值
},
muctions:{},
actions:{}
}
export default counter
在vuex里面引入这个文件
import { createStore } from "vuex";
import counter from "./modules/home"
// 创建 Vuex store
const store = createStore({
// state 中存放全局共享的数据
state() {
return {
counter: 0, // 初始化 counter 的值
};
},
// 可以在这里添加 mutations、actions 和 getters
mutations: {},
actions: {}
},
getters: {},
modules:{
home:counter
}
});
// 导出 store
export default store;
数据的模板需要改变,其他不用改变
<p>Counter: {{ $store.state.home.banner}}</p>
默认和命名空间
默认情况下,action 和muctions仍然是注册在全局的命名空间中,如果使用多个模块的时候,命名要非常的注意,如果想要互不影响就需要命名空间的约束
const counter = {
namespaced:true,//命名空间
state() {
return {
counter: [], // 初始化 counter 的值
},
muctions:{},
actions:{}
}
export default counter
读取时,所有都要加模块才能读取到
<p>Counter: {{ $store.getter['counter/方法名']}}</p>
派发需要这么做
function incrementcount(){
store.dispatch("counter/crementCountAction")
}
好啦学完vuex啦,真棒!!