npm install vuex@next


<template>
<h2>{{ counter }}</h2>
<button @click="counter + 1">加</button>
<button @click="counter - 1">减</button>
</template>
<script>
export default {
data() {
return {
counter: 0,
};
},
};
</script>
<style>
</style>

Vue已经帮我们做好了单个界面的状态管理,但是如果是多个界面呢?
也就是说对于某些状态(状态1/状态2/状态3)来说只属于我们某一个试图,但是也有一些状态(状态a/状态b/状态c)属于多个试图共同想要维护的
全局单例模式(大管家)
图解

每一个Vuex应用的核心就是store(仓库):
Vuex和单纯的全局对象有什么区别呢?
改变store中的状态的唯一途径就显示提交(commit) mutation;
使用步骤:
创建Store对象=>Vue3
import { createStore } from "vuex";
const stort = createStore({
state() {
return {
counter:0
}
},
});
export default stort;
import vuex from './stort/index';
createApp(App).use(vuex).mount('#app')
App.vue =>委托给mutations进行处理
<template>
<div class="id">
<home />
<br>
<h2>{{ $store.state.counter }}</h2>
<button @click="addClick()">加一</button>
<button @click="redClick()">减一</button>
</div>
</template>
<script>
export default {
// 基本的状态管理实现
methods: {
addClick() {
// 进行委托给Mutations处理=>委托的是事件不是数据
this.$store.commit("addClick");
},
redClick() {
this.$store.commit("redClick");
},
},
};
</script>
<style>
</style>
Options API)概念:
export default new Vuex.Store({
state: {
count: 0
}
}
State数据访问方式一 :
<h3>当前最新Count值为:{{this.$store.state.count}}</h3>
State数据访问方式二 :
import { mapState } from 'vuex'
<template>
<div>
<h3>当前最新Count值为:{{ count }}</h3>
<button>-1</button>
</div>
</template>
<script>
import { mapState } from "vuex";
export default {
computed: {
...mapState(["count"])
//上面的映射等价于
// count(){
// return this.$store.state.user
// }
//对象写法
// ...mapState(["counte"])
...mapState({
count: state => state.counte,
})
}
};
</script>
<template>
<div>
<h3>当前最新Count值为:{{this.$store.state.count}}</h3>
<button @click="add">+1</button>
</div>
</template>
<script>
export default {
methods: {
add() {
this.$store.state.count++;
}
}
};
</script>
(检测不到数据的变化),那要怎么做呢?这时,就需要使用Mutation了。利用Mutation用于变更存储在Store中的数据。
注意点:
下图通过devtools工具监控

在mutations中定义函数,如下:
mutations: {
// 自增
add(state) {
state.count++
}
}
this.$store.commit(方法名)完成触发,如下:mutations: {
// 自增
add(state) {
state.count++
}
}
import { mapMutations } from 'vuex'
methods: {
...mapMutations(["increment", "decrement"]),
//对象写法
...mapMutations({
add: "increment"
})
},
Payload。

mutations中定义函数时,同样可以接收参数,示例如下:mutations: {
// 自增
add(state) {
state.count++
},
// 带参数
addNum(state, payload) {
state.count += payload.number
}
}
App.vue组件调用
methods: {
add() {
// this.$store.state.count++;
this.$store.commit("add");
},
addNum() {
this.$store.commit("addNum", {
number: 10
});
}
}
解决方案
mutation-types.js文件,在其中定义常量
在store/index.js中引入并使用:import Vue from 'vue'
import Vuex from 'vuex'
import * as types from './mutation-type'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
count: 0,
user: {
name: '旺财',
age: 12
}
},
mutations: {
// 自增
[types.ADD_NUM](state) {
state.count++
},
}
在组件中,引入并调用:
<script>
import { ADD_NUM } from "../store/mutation-type";
export default {
methods: {
add() {
this.$store.commit(ADD_NUM);
// this.addAsync();
// this.$store.state.count++;
// this.$store.commit("add");
}
}
};
</script>
参数context
在actions中定义的方法,都会有默认值context
context是和store对象具有相同方法和属性的对象
可以通过context进行commit相关操作,可以获取context.state数据
注意点
在 action 中,不能直接修改 state 中的数据
必须通过 context.commit() 触发某个 mutation 才行
使用方式一 :
export default new Vuex.Store({
state: {
count: 0
},
//只有 mutations 中定义的函数,才有权力修改 state 中的数据
mutations: {
// 自增
add(state) {
state.count++
}
},
actions: {
addAsync(context) {
setTimeout(() => {
//在 action 中,不能直接修改 state 中的数据
//必须通过 context.commit() 触发某个 mutation 才行
context.commit('add')
}, 1000);
}
}
})
dispatch派发<script>
export default {
methods: {
addNumSync(){
// dispatch函数 专门用于触发 Action
this.$store.dispatch('addAsync')
}
}
};
</script>
import { mapActions } from 'vuex'
<script>
import { mapActions } from "vuex";
export default {
methods: {
...mapActions(["addAsync"]),
add() {Î
this.addAsync()
},
}
...mapActions(["addAsync"]),
---------------------------
<button @click="addAsync">+1(异步)</button>
index.js的actions中,增加携带参数方法,如下:export default new Vuex.Store({
state: {
count: 0
},
mutations: {
// 带参数
addNum(state, payload) {
state.count += payload.number
}
},
actions: {
addAsyncParams(context, payload) {
setTimeout(() => {
context.commit('addNum', payload)
}, 1000);
}
}
})
methods: {
addNumSyncParams() {
this.$store.dispatch("addAsyncParams", {
number: 100
});
}
}
Promise经常用于异步操作,在Action中,可以将异步操作放在Promise中,并且在成功或失败后,调用对应的resolve或reject。
actions: {
loadUserInfo(context){
return new Promise((resolve)=>{
setTimeout(() => {
context.commit('add')
resolve()
}, 2000);
})
}
}
methods: {
addPromise() {
this.$store.dispatch("loadUserInfo").then(res => {
console.log("done");
});
}
}
Getters用于对Store中的数据进行加工处理形成新的数据,类似于Vue中的计算属性Store中数据发生变化,Getters的数据也会跟随变化//定义 Getter
const store = new Vuex.Store({
state:{
count: 0
},
getters:{
showNum(state){
return '当前Count值为:['+state.count']'
}
}
})
<h3>{{ this.$store.getters.showNum }}</h3>
import { mapGetters } from 'vuex'
computed: {
...mapGetters(["showNum"])
}
<h3>{{ showNum }}</h3>
基本使用
export default {
state: {
name: '凤凰于飞'
},
actions: {
aUpdateName(context) {
setTimeout(() => {
context.commit('updateName', '旺财')
}, 1000);
}
},
mutations: {
updateName(state, payload) {
state.name = payload
}
},
getters: {
fullName(state) {
return state.name + '王昭君'
},
fullName2(state, getters) {
// 通过getters调用本组方法
return getters.fullName + ' 礼拜'
},
fullName3(state, getters, rootState) {
// state代表当前module数据状态,rootState代表根节点数据状态
return getters.fullName2 + rootState.counter
}
}
}
注意 局部状态通过context.state暴露出来,根节点状态则为context.rootState
import Vue from "vue"
import Vuex from "vuex"
import moduleA from './modules/moduleA'
Vue.use(Vuex)
const store = new Vuex.Store({
modules: {
a: moduleA
}
})
export default store
获取数据项: {{$store.state.模块名.数据项名}}
获取getters: {{$store.getters['模块名/getters名']}}


访问模块中的mutations/actions:
namespaced为true,则需要额外去补充模块名namespaced为false,则不需要额外补充模块名$store.commit('mutations名') // namespaced为false
$store.commit('模块名/mutations名') // namespaced为true
注意 如果项目非常复杂,除了分模块划分外,还可以将主模块的actions、mutations、getters等分别独立出去,拆分成单独的js文件,分别通过export导出,然后再index.js中导入使用。
export default{
aUpdateInfo(context, payload) {
return new Promise((resolve, reject) => {
setTimeout(() => {
context.commit('updateInfo')
resolve()
}, 1000);
})
}
}
import Vue from "vue"
import Vuex from "vuex"
import mutations from './mutations'
import actions from './actions'
import getters from './getters'
import moduleA from './modules/moduleA'
Vue.use(Vuex)
const state = {
counter: 1000,
students: [
{ id: 1, name: '旺财', age: 12 },
{ id: 2, name: '小强', age: 31 },
{ id: 3, name: '大明', age: 45 },
{ id: 4, name: '狗蛋', age: 78 }
],
info: {
name: 'keko'
}
}
const store = new Vuex.Store({
state,
mutations,
getters,
actions,
modules: {
a: moduleA
}
})
export default store
模块项目图

##2. 6 辅助函数使用汇总
直接使用: this.$store.state.xxx;
map辅助函数:
computed: {
...mapState(['xxx']),
...mapState({'新名字': 'xxx'})
}
直接使用: this.$store.state.模块名.xxx;
map辅助函数:
computed: {
...mapState('模块名', ['xxx']),
...mapState('模块名', {'新名字': 'xxx'})
}
直接使用:this.$store.getters.xxx
map辅助函数:
computed: {
...mapGetters(['xxx']),
...mapGetters({'新名字': 'xxx'})
}
直接使用: this.$store.getters.模块名.xxx
map辅助函数:
computed: {
...mapGetters('模块名', ['xxx']),
...mapGetters('模块名',{'新名字': 'xxx'})
}
直接使用:this.$store.commit('mutation名', 参数)
map辅助函数:
methods: {
...mapMutations(['mutation名']),
...mapMutations({'新名字': 'mutation名'})
}
直接使用: this.$store.commit('模块名/mutation名', 参数)
map辅助函数:
methods: {
...mapMutations('模块名', ['xxx']),
...mapMutations('模块名',{'新名字': 'xxx'})
}
直接使用:this.$store.dispatch('action名', 参数)
map辅助函数:
methods: {
...mapActions(['actions名']),
...mapActions({'新名字': 'actions名'})
}
直接使用: this.$store.dispatch('模块名/action名', 参数)
map辅助函数:
methods: {
...mapActions('模块名', ['xxx']),
...mapActions('模块名',{'新名字': 'xxx'})
}
CPA)注意点
setup没有this
useStore方法,就可以在setup中使用vuex的相关函数了基本使用参考代码
<template>
<div>
<h2>{{ $store.state.count }}</h2>
<button @click="plusCount">点击</button>
</div>
</template>
<script>
import { useStore } from "vuex";
export default {
setup(props, context) {
const store = useStore(); // 使用useStore方法
console.log(store);
function plusCount() {
store.commit("increaseCount");
}
return { plusCount };
},
};
</script>
导入函数
import { mapState, useStore } from 'vuex'
import { computed } from 'vue'
基本使用
setup() {
const store = useStore()
const sCounter = computed(() => store.state.counter)
//必须return
return {
sCounter,
}
}
创建useStart.js封装函数
import { computed } from 'vue'
import { mapState, useStore } from 'vuex'
export function useState(mapper) {
// 拿到store独享
const store = useStore();
// 获取到对应的对象的functions: {name: function, age: function}
const storeStateFns = mapState(mapper);
// 对数据进行转换
const storeState = {};
// 抽取key进行遍历
Object.keys(storeStateFns).forEach((fnKey) => {
// 把key给storeStateFns函数=>并绑定this
const fn = storeStateFns[fnKey].bind({ $store: store });
// 函数给计算属性并赋值给storeState对象
storeState[fnKey] = computed(fn);
});
return storeState;
}
import { useState } from '../hooks/useState'
export default {
setup() {
const storeState = useState(["counter", "name", "age", "height"])
const storeState2 = useState({
sCounter: state => state.counter,
sName: state => state.name
})
return {
...storeState,
...storeState2
}
}
}
<h2>{{counter}}</h2>
<h2>{{name}}</h2>
(相当于computed属性)

getters可以接收第二个参数:

getters中的函数本身,可以返回一个函数,那么在使用的地方相当于可以调用这个函数:

index.js中state与Getter代码
state() {
return {
counter: 100,
name: "why",
age: 18,
height: 1.88,
books: [
{ name: "深入Vuejs", price: 200, count: 3 },
{ name: "深入Webpack", price: 240, count: 5 },
{ name: "深入React", price: 130, count: 1 },
{ name: "深入Node", price: 220, count: 2 },
],
discount: 0.6,
banners: []
};
},
getters: {
totalPrice(state, getters) {
let totalPrice = 0
for (const book of state.books) {
totalPrice += book.count * book.price
}
return totalPrice * getters.currentDiscount
},
currentDiscount(state) {
return state.discount * 0.9
},
totalPriceCountGreaterN(state, getters) {
return function(n) {
let totalPrice = 0
for (const book of state.books) {
if (book.count > n) {
totalPrice += book.count * book.price
}
}
return totalPrice * getters.currentDiscount
}
},
<h2>总价值: {{ $store.getters.totalPrice }}</h2>
<h2>总价值: {{ $store.getters.totalPriceCountGreaterN(1) }}</h2>
创建useGetters.js
import { computed } from 'vue'
import { mapGetters, useStore } from 'vuex'
export function useGetters(mapper) {
// 拿到store独享
const store = useStore()
// 获取到对应的对象的functions: {name: function, age: function}
const storeStateFns = mapGetters(mapper)
// 对数据进行转换
const storeState = {}
Object.keys(storeStateFns).forEach(fnKey => {
const fn = storeStateFns[fnKey].bind({$store: store})
storeState[fnKey] = computed(fn)
})
return storeState
}
<template>
<h2>{{ nameInfo }}</h2>
<h2>{{ ageInfo }}</h2>
<h2>{{ heightInfo }}</h2>
</template>
import { useGetters } from '../hooks/useGetters'
export default {
computed: {
},
setup() {
const storeGetters = useGetters(["nameInfo", "ageInfo", "heightInfo"])
return {
...storeGetters
}
}
}
index.js中为Actions添加异步方法getHomeMultidata(context) {
return new Promise((resolve, reject) => {
axios.get("http://123.207.32.32:8000/home/multidata").then(res => {
context.commit("addBannerData", res.data.data.banner.list)
resolve({name: "xiazhan", age: 18})
}).catch(err => {
reject(err)
})
})
}
setup() {
const store = useStore()
onMounted(() => {
const promise = store.dispatch("getHomeMultidata")
promise.then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
})
}


namespaced: true 的方式使其成为带命名空间的模块:在模块module文件夹中创建home.js
const homeModule = {
// 指定命名空间 =>局部
namespaced: true,
state() {
return {
homeCounter: 100
}
},
getters: {
doubleHomeCounter(state, getters, rootState, rootGetters) {
return state.homeCounter * 2
},
otherGetter(state) {
return 100
}
},
mutations: {
increment(state) {
state.homeCounter++
}
},
actions: {
incrementAction({commit, dispatch, state, rootState, getters, rootGetters}) {
commit("increment")
commit("increment", null, {root: true})
}
}
}
export default homeModule
在入口文件index.js导入home.js
import { createStore } from "vuex"
import home from './modules/home'
import user from './modules/user'
const store = createStore({
state() {
return {
rootCounter: 100
}
},
getters: {
doubleRootCounter(state) {
return state.rootCounter * 2
}
},
mutations: {
increment(state) {
state.rootCounter++
}
},
modules: {
home,
user
}
});
export default store;
在组件中访问home.js' 中的counte和action的数据
<template>
<div>
//[查找规则]
<h2>{{ $store.getters["home/doubleHomeCounter"] }}</h2>
<button @click="homeIncrement">home+1</button>
<button @click="homeIncrementAction">home+1</button>
</div>
</template>
<script>
export default {
methods: {
homeIncrement() {
this.$store.commit("home/increment")
},
homeIncrementAction() {
this.$store.dispatch("home/incrementAction")
}
}
}
</script>
<style scoped>
</style>

参考代码
actions: {
incrementAction({commit, dispatch, state, rootState, getters, rootGetters}) {
commit("increment")
commit("increment", null, {root: true})
}
}



setup中的使用 setup() {
// {homeCounter: function}
const state = useState(["rootCounter"])
const rootGetters = useGetters(["doubleRootCounter"])
const getters = useGetters("home", ["doubleHomeCounter"])
const mutations = mapMutations(["increment"])
const actions = mapActions(["incrementAction"])
return {
...state,
...getters,
...rootGetters,
...mutations,
...actions
}
}
Vuex 并不限制你的代码结构。但是,它规定了一些需要遵守的规则:
只要你遵守以上规则,如何组织代码随你便。如果你的 store 文件太大,只需将 action、mutation 和 getter 分割到单独的文件。
对于大型应用,我们会希望把 Vuex 相关代码分割到模块中。下面是项目结构示例:

一、Vuex的五个核心概念:state、getters、mutations、actions、modules
1、state: vuex的基本数据,用来存储变量;
2、getters: 从基本数据(state)派生的数据,相当于state的计算属性;
3、mutations: 提交更新数据的方法,必须是同步的(如果需要异步使用action)。每个mution 都有一个字符串的事件类型(type)和一个回调函数(handler)。
回调函数就是我们实际进行状态更改的地方,并且它会接受 state作为第一个参数,提交载荷作为第二个参数。
4、action: 和mution的功能大致相同,不同之处在于 ①Action提交的是mution,而不是直接变更状态,②Action可以包含任意异步操作。
5、modules: 模块化vuex,可以让每一个模块拥有自己的 state、mutation、action、 getters,使得结构非常清晰,方便管理。
图解

参考: vue中使用vuex(超详细)
vuex基础解析
vuex官网