什么是状态管理
复杂的状态管理
Vuex的状态管理
Vuex的状态管理
Vuex的安装
创建Store
组件中使用store
单一状态树
组件获取状态
在setup中使用mapState
getters的基本使用
getters第二个参数
getters的返回函数
mapGetters的辅助函数
Mutation基本使用
Mutation携带数据
Mutation常量类型
mapMutations辅助函数
mutation重要原则
actions的基本使用
actions的分发操作
actions的辅助函数
actions的异步操作
module的基本使用
module的局部状态
module的命名空间
module修改或派发根组件
module的辅助函数
module的辅助函数
对useState和useGetters修改
目录结构
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>