什么是状态管理
![](https://img2020.cnblogs.com/blog/1877004/202107/1877004-20210726194230918-35836811.png)
复杂的状态管理
![](https://img2020.cnblogs.com/blog/1877004/202107/1877004-20210726194244939-1763821134.png)
Vuex的状态管理
![](https://img2020.cnblogs.com/blog/1877004/202107/1877004-20210726194302428-879091882.png)
Vuex的状态管理
![](https://img2020.cnblogs.com/blog/1877004/202107/1877004-20210726194319486-1964763748.png)
Vuex的安装
![](https://img2020.cnblogs.com/blog/1877004/202107/1877004-20210726194334562-114621362.png)
创建Store
![](https://img2020.cnblogs.com/blog/1877004/202107/1877004-20210726194354517-1490445475.png)
组件中使用store
![](https://img2020.cnblogs.com/blog/1877004/202107/1877004-20210726194415430-2008138442.png)
![](https://img2020.cnblogs.com/blog/1877004/202107/1877004-20210726194436632-2004919879.png)
![](https://img2020.cnblogs.com/blog/1877004/202107/1877004-20210726194505499-853335636.png)
单一状态树
![](https://img2020.cnblogs.com/blog/1877004/202107/1877004-20210726194521072-1931374054.png)
组件获取状态
![](https://img2020.cnblogs.com/blog/1877004/202107/1877004-20210726194547799-2128512215.png)
在setup中使用mapState
![](https://img2020.cnblogs.com/blog/1877004/202107/1877004-20210726194621972-1611171065.png)
getters的基本使用
![](https://img2020.cnblogs.com/blog/1877004/202107/1877004-20210726194717550-1324148062.png)
getters第二个参数
![](https://img2020.cnblogs.com/blog/1877004/202107/1877004-20210726194734594-1043005616.png)
getters的返回函数
![](https://img2020.cnblogs.com/blog/1877004/202107/1877004-20210726194749441-586335186.png)
mapGetters的辅助函数
![](https://img2020.cnblogs.com/blog/1877004/202107/1877004-20210726194818915-1458688235.png)
Mutation基本使用
![](https://img2020.cnblogs.com/blog/1877004/202107/1877004-20210726194831652-1687099358.png)
Mutation携带数据
![](https://img2020.cnblogs.com/blog/1877004/202107/1877004-20210726194912383-771744877.png)
Mutation常量类型
![](https://img2020.cnblogs.com/blog/1877004/202107/1877004-20210726194941968-2002018702.png)
mapMutations辅助函数
![](https://img2020.cnblogs.com/blog/1877004/202107/1877004-20210726195000994-2022656996.png)
mutation重要原则
![](https://img2020.cnblogs.com/blog/1877004/202107/1877004-20210726195014788-1239871772.png)
actions的基本使用
![](https://img2020.cnblogs.com/blog/1877004/202107/1877004-20210726195030693-61955483.png)
actions的分发操作
![](https://img2020.cnblogs.com/blog/1877004/202107/1877004-20210726195052996-1981442121.png)
actions的辅助函数
![](https://img2020.cnblogs.com/blog/1877004/202107/1877004-20210726195108684-404220117.png)
actions的异步操作
![](https://img2020.cnblogs.com/blog/1877004/202107/1877004-20210726195127711-1014057035.png)
module的基本使用
![](https://img2020.cnblogs.com/blog/1877004/202107/1877004-20210726195152461-1519508267.png)
module的局部状态
![](https://img2020.cnblogs.com/blog/1877004/202107/1877004-20210726195222576-1288229979.png)
module的命名空间
![](https://img2020.cnblogs.com/blog/1877004/202107/1877004-20210726195337787-1840009334.png)
module修改或派发根组件
![](https://img2020.cnblogs.com/blog/1877004/202107/1877004-20210726195402001-1196234577.png)
module的辅助函数
![](https://img2020.cnblogs.com/blog/1877004/202107/1877004-20210726195433360-809203757.png)
module的辅助函数
![](https://img2020.cnblogs.com/blog/1877004/202107/1877004-20210726195458547-486959246.png)
对useState和useGetters修改
![](https://img2020.cnblogs.com/blog/1877004/202107/1877004-20210726195511082-1347873659.png)
目录结构
![](https://img2020.cnblogs.com/blog/1877004/202107/1877004-20210726195617305-1045630282.png)
main.js
import { createApp } from 'vue'
import App from './App_nexttick.vue'
import store from './store'
createApp(App).use(store).mount('#app')
App.vue
<template>
<home />
<h2>App: {{ $store.state.counter }}</h2>
<button @click="increment">+1</button>
<button @click="decrement">-1</button>
</template>
<script>
import Home from './pages/Home.vue'
export default {
name: 'App',
components: {
Home,
},
methods: {
increment() {
this.$store.commit('increment')
},
decrement() {
this.$store.commit('decrement')
},
},
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
hooks/index.js
import { useGetters } from './useGetters'
import { useState } from './useState'
export { useGetters, useState }
hooks/useMapper.js
import { computed } from 'vue'
import { mapState, useStore } from 'vuex'
export function useMapper(mapper, mapFn) {
// 拿到store独享
const store = useStore()
// 获取到对应的对象的functions: {name: function, age: function}
const storeStateFns = mapFn(mapper)
// 对数据进行转换
const storeState = {}
Object.keys(storeStateFns).forEach(fnKey => {
const fn = storeStateFns[fnKey].bind({ $store: store })
storeState[fnKey] = computed(fn)
})
return storeState
}
hooks/useState copy.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 = {}
Object.keys(storeStateFns).forEach(fnKey => {
const fn = storeStateFns[fnKey].bind({ $store: store })
storeState[fnKey] = computed(fn)
})
return storeState
}
hooks/useState.js
import { mapState, createNamespacedHelpers } from 'vuex'
import { useMapper } from './useMapper'
export function useState(moduleName, mapper) {
let mapperFn = mapState
if (typeof moduleName === 'string' && moduleName.length > 0) {
mapperFn = createNamespacedHelpers(moduleName).mapState
} else {
mapper = moduleName
}
return useMapper(mapper, mapperFn)
}
hooks/useState copy.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 = {}
Object.keys(storeStateFns).forEach(fnKey => {
const fn = storeStateFns[fnKey].bind({ $store: store })
storeState[fnKey] = computed(fn)
})
return storeState
}
hooks/useState.js
import { mapState, createNamespacedHelpers } from 'vuex'
import { useMapper } from './useMapper'
export function useState(moduleName, mapper) {
let mapperFn = mapState
if (typeof moduleName === 'string' && moduleName.length > 0) {
mapperFn = createNamespacedHelpers(moduleName).mapState
} else {
mapper = moduleName
}
return useMapper(mapper, mapperFn)
}
store-root/index.js
import { createStore } from 'vuex'
import axios from 'axios'
import { INCREMENT_N } from './mutation-types'
const store = createStore({
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
/*
state.books.reduce((prev, book) => {
if (book.count <= n) {
return totalPrice
} else {
totalPrice = prev + book.price * book.count
return totalPrice
}
// 踩坑:下面这样写,当book.count <= n时,返回的是undefined,相加结果是NaN,结果是NaN就停止计算
if (book.count > n) {
totalPrice = prev + book.price * book.count
console.log('totalPrice--', totalPrice)
return totalPrice
}
}, 0)
*/
}
},
nameInfo(state) {
return `name: ${state.name}`
},
ageInfo(state) {
return `age: ${state.age}`
},
heightInfo(state) {
return `height: ${state.height}`
},
},
mutations: {
increment(state) {
state.counter++
},
decrement(state) {
state.counter--
},
// {n: 10, name: "why", age: 18} -> payload
[INCREMENT_N](state, payload) {
console.log(payload)
state.counter += payload.n
},
addBannerData(state, payload) {
state.banners = payload
},
},
actions: {
// 放函数
// 1.参数问题
incrementAction(context, payload) {
console.log(payload)
setTimeout(() => {
context.commit('increment')
}, 1000)
},
// 2.context的其他属性
decrementAction({ commit, dispatch, state, rootState, getters, rootGetters }) {
commit('decrement')
},
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: 'hahaha', age: 18 })
})
.catch(err => {
reject(err)
})
})
},
},
})
export default store
mutation-types.js
export const INCREMENT_N = "increment_n"
store/index.js
import { createStore } from 'vuex'
// **注意: 命名、导出都是homeModule、userModule这种风格,但是导入的时候,要去掉后面的Module
import home from './modules/home'
import user from './modules/user'
const store = createStore({
state() {
return {
rootCounter: 0,
}
},
mutations: {
increment(state) {
state.rootCounter++
},
},
modules: {
home,
user,
},
})
export default store
modules/home.js
const homeModule = {
namespaced: true, // 【module的命名空间。】
state() {
return {
homeCounter: 100,
}
},
getters: {
// 【有4个参数。】
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')
// 【module修改或派发根组件】
commit('increment', null, { root: true }) // 对根store的提交
// dispatch
// dispatch("incrementAction", null, {root: true})
},
},
}
export default homeModule
user.js
const userModule = {
namespaced: true,
state() {
return {
userCounter: 10,
}
},
getters: {},
mutations: {},
actions: {},
}
export default userModule
01_mapState_compunted.vue
<template>
<div>
<h2>Home:{{ $store.state.counter }}</h2>
<h2>Home:{{ sCounter }}</h2>
<h2>Home:{{ sName }}</h2>
<!-- <h2>Home:{{ age }}</h2>
<h2>Home:{{ height }}</h2> -->
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
computed: {
fullName() {
return 'Kobe Bryant'
},
// 其他的计算属性, 从state获取
// ...mapState(["counter", "name", "age", "height"])
...mapState({
sCounter: state => state.counter,
sName: state => state.name,
}),
},
}
</script>
<style scoped></style>
02_mapState_setup.vue
<template>
<div>
<h2>Home:{{ $store.state.counter }}</h2>
<hr />
<h2>{{ sCounter }}</h2>
<h2>{{ counter }}</h2>
<h2>{{ name }}</h2>
<h2>{{ age }}</h2>
<h2>{{ height }}</h2>
<hr />
</div>
</template>
<script>
import { computed } from 'vue'
import { mapState, useStore } from 'vuex'
export default {
computed: {
fullName: function () {
return '1fdasfdasfad'
},
...mapState(['name', 'age']),
},
setup() {
const store = useStore()
const sCounter = computed(() => store.state.counter)
// const sName = computed(() => store.state.name)
// const sAge = computed(() => store.state.age)
// {name: function, age: function, height: function}
const storeStateFns = mapState(['counter', 'name', 'age', 'height'])
// {name: ref, age: ref, height: ref}
const storeState = {}
Object.keys(storeStateFns).forEach(fnKey => {
const fn = storeStateFns[fnKey].bind({ $store: store })
storeState[fnKey] = computed(fn)
})
return {
sCounter,
...storeState,
}
},
}
</script>
<style scoped></style>
03_useState封装后使用.vue
<template>
<div>
<h2>Home:{{ $store.state.counter }}</h2>
<hr />
<h2>{{ counter }}</h2>
<h2>{{ name }}</h2>
<h2>{{ age }}</h2>
<h2>{{ height }}</h2>
<h2>{{ sCounter }}</h2>
<h2>{{ sName }}</h2>
<hr />
</div>
</template>
<script>
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,
}
},
}
</script>
<style scoped></style>
04_mapGetters_computed.vue
<template>
<div>
<h2>总价值: {{ $store.getters.totalPrice }}</h2>
<h2>总价值: {{ $store.getters.totalPriceCountGreaterN(1) }}</h2>
<hr />
<h2>{{ sNameInfo }}</h2>
<h2>{{ sAgeInfo }}</h2>
<!-- <h2>{{ ageInfo }}</h2>
<h2>{{ heightInfo }}</h2> -->
<hr />
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
computed: {
...mapGetters(['nameInfo', 'ageInfo', 'heightInfo']),
...mapGetters({
sNameInfo: 'nameInfo',
sAgeInfo: 'ageInfo',
}),
},
setup() {},
}
</script>
05_useGetters封装后使用.vue
<template>
<div>
<h2>总价值: {{ $store.getters.totalPrice }}</h2>
<h2>总价值: {{ $store.getters.totalPriceCountGreaterN(1) }}</h2>
<hr />
<h2>{{ nameInfo }}</h2>
<h2>{{ ageInfo }}</h2>
<h2>{{ heightInfo }}</h2>
<hr />
</div>
</template>
<script>
import { useGetters } from '../hooks/useGetters'
export default {
computed: {},
setup() {
const storeGetters = useGetters(['nameInfo', 'ageInfo', 'heightInfo'])
return {
...storeGetters,
}
},
}
</script>
06_mutation的细节补充.vue
<template>
<div>
<h2>当前计数: {{ $store.state.counter }}</h2>
<hr />
<button @click="$store.commit('increment')">+1</button>
<button @click="$store.commit('decrement')">-1</button>
<button @click="addTen">+10</button>
<hr />
</div>
</template>
<script>
import { INCREMENT_N } from '../store/mutation-types'
export default {
methods: {
addTen() {
// this.$store.commit('incrementN', 10)
// this.$store.commit('incrementN', {n: 10, name: "why", age: 18})
// 另外一种提交风格
this.$store.commit({
type: INCREMENT_N,
n: 10,
name: 'why',
age: 18,
})
},
},
}
</script>
07_mutations的辅助函数.vue
<template>
<div>
<h2>当前计数: {{ $store.state.counter }}</h2>
<hr />
<button @click="increment">+1</button>
<button @click="add">+1</button>
<button @click="decrement">-1</button>
<button @click="increment_n({ n: 10 })">+10</button>
<!-- 加的 -->
<button @click="addN">Home addN +n</button>
<button @click="addOne">Home addOne +1</button>
<hr />
</div>
</template>
<script>
import { mapMutations, mapState } from 'vuex'
import { INCREMENT_N } from '../store/mutation-types'
export default {
methods: {
...mapMutations(['increment', 'decrement', INCREMENT_N]),
...mapMutations({
add: 'increment',
}),
// 下面的代码是加的
addN() {
this.increament_n({ num: 30 }) // 加上this啊
// this[INCREAMENT_N]({num: 30}) // 这样写也可以
},
addOne() {
// 【mapMutations就是把方法映射到当前组件中,所以可以通过this访问方法。】
this.increament()
},
},
setup() {
const storeMutations = mapMutations(['increment', 'decrement', INCREMENT_N])
return {
...storeMutations,
}
},
}
</script>
<style scoped></style>
08_actions的使用和细节补充.vue
<template>
<div>
<h2>当前计数: {{ $store.state.counter }}</h2>
<hr />
<button @click="increment">+1</button>
<button @click="decrement">-1</button>
<hr />
</div>
</template>
<script>
import axios from 'axios'
export default {
methods: {
increment() {
this.$store.dispatch('incrementAction', { count: 100 })
},
decrement() {
// 3.派发风格(对象类型)
this.$store.dispatch({
type: 'decrementAction',
})
},
},
mounted() {
this.$store.dispatch('getHomeMultidata')
},
setup() {},
}
</script>
<style scoped></style>
09_actions的辅助函数.vue
<template>
<div>
<h2>当前计数: {{ $store.state.counter }}</h2>
<hr />
<button @click="incrementAction">+1</button>
<button @click="decrementAction">-1</button>
<button @click="add">+1</button>
<button @click="sub">-1</button>
<hr />
</div>
</template>
<script>
import { mapActions } from 'vuex'
export default {
methods: {
// ...mapActions(["incrementAction", "decrementAction"]),
// ...mapActions({
// add: "incrementAction",
// sub: "decrementAction"
// })
},
setup() {
const actions = mapActions(['incrementAction', 'decrementAction'])
const actions2 = mapActions({
add: 'incrementAction',
sub: 'decrementAction',
})
return {
...actions,
...actions2,
}
},
}
</script>
10_actions的返回Promise.vue
<template>
<div>
<h2>当前计数: {{ $store.state.counter }}</h2>
<hr />
<button @click="incrementAction">+1</button>
<button @click="decrementAction">-1</button>
<button @click="add">+1</button>
<button @click="sub">-1</button>
<hr />
</div>
</template>
<script>
import { onMounted } from 'vue'
import { useStore } from 'vuex'
export default {
setup() {
const store = useStore()
onMounted(() => {
const promise = store.dispatch('getHomeMultidata')
promise.then(res => console.log(res)).catch(err => console.log(err))
})
},
}
</script>
11_module的基本使用.vue
<template>
<div>
<h2>{{ $store.state.rootCounter }}</h2>
<h2>{{ $store.state.home.homeCounter }}</h2>
<h2>{{ $store.state.user.userCounter }}</h2>
</div>
</template>
<script>
export default {
setup() {},
}
</script>
12_module的命名空间.vue
<template>
<div>
<h2>root: {{ $store.state.rootCounter }}</h2>
<h2>home: {{ $store.state.home.homeCounter }}</h2>
<h2>user: {{ $store.state.user.userCounter }}</h2>
<hr />
<!-- 【store.getters[]括号中是字符串,字符串包括 模块名/getters名。而store.state后面直接跟模块名,store.state.login.userMenus。】 -->
<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>