Vue3
一、state
setup中使用mapState
vue2中
computed() {
...mapState(['xx']) // 一个个函数
}
方法一
通过
useStore拿到store后去获取某个状态即可
const sCounter = computed(() => store.state.counter)
方法二
使用
mapState的功能
setup() {
const store = useStore()
const sCounter = computed(() => store.state.counter)
const storeStateFn = mapState(['counter', 'name', 'age', 'height'])
const storeState = {}
Object.keys(storeStateFn).forEach(fnKey => {
/**
* 报错原因:
* fnKey 沒有this
* mapState的本质还是通过this.$store.state.xxx获取
*/
const fn = storeState[fnKey]
storeState[fnKey] = computed(fn)
})
return {
sCounter,
...storeState
}
}
解决
bind({$store: store})
setup() {
const store = useStore()
const sCounter = computed(() => store.state.counter)
const storeStateFn = mapState(['counter', 'name', 'age', 'height'])
const storeState = {}
Object.keys(storeStateFn).forEach(fnKey => {
/**
* fnKey 沒有this
* mapState的本质还是通过this.$store.state.xxx获取
* 解决:bind({$store: store}) 指向
*/
const fn = storeStateFn[fnKey].bind({$store: store})
storeState[fnKey] = computed(fn)
})
return {
sCounter,
...storeState
}
}
封装
Vuex并没有提供非常方便的使用mapState的方式,这里进行了一个函数的封装
import { computed } from "vue"
import { mapState, useStore } from "vuex"
export function useState(mapper) {
// 拿到store独享
const store = useStore()
// 获取到对应的对象的functions: {name: function, age: function}
// mapState: 支持数组和对象
const storeStateFn = mapState(mapper)
// 对数据进行转换
const storeState = {}
Object.keys(storeStateFn).forEach(fnKey => {
console.log(fnKey)
const fn = storeStateFn[fnKey].bind({$store: store})
storeState[fnKey] = computed(fn)
})
return storeState
}
<template>
<div>
<div>home</div>
<hr>
<div>{{sCouter}}</div>
<hr>
<div>{{sName}}</div>
<hr>
<div>{{age}}</div>
<hr>
<div>{{height}}</div>
<hr>
<br />
</div>
</template>
<script>
import { useState } from "@/hooks/useState";
export default {
name: "",
setup() {
const storeState = useState(["age", "height"]);
// 传对象可对其进行重命名,避免命名冲突
const storeState2 = useState({
sCouter: state => state.counter,
sName: state => state.name
})
return {
...storeState,
...storeState2
};
},
};
</script>
二、getters
import { createStore } from 'vuex'
const store = createStore({
state() {
return {
counter: 0,
name: '小明',
age: 18,
height: 188,
books: [
{ name: 'vue.js', price: 88, count: 1 },
{ name: 'ts', price: 78, count: 2 },
{ name: 'javaScript', price: 68, count: 3 }
],
discount: 0.8
}
},
getters: {
totalPrice(state) {
let totalPrice = 0
for (const book of state.books) {
// console.log('book', book)
totalPrice += book.count * book.price
}
return totalPrice
},
currentDiscount(state) {
return state.discount * 0.9
},
/**
getters本身不能接收参数
*/
totalPriceCountGreaterN(state, getters) {
return function (n) {
/**
n为接收的参数
*/
let totalPrice = 0
for (const book of state.books) {
// console.log('book', book)
if (book.count > n) {
totalPrice += book.count * book.price * getters.currentDiscount
} else {
totalPrice += book.count * book.price
}
}
return totalPrice
}
}
}
})
export default store
<div>{{$store.getters.totalPrice}}</div>
<div>{{$store.getters.totalPriceCountGreaterN(1)}}</div>
使用mapGetters
optionsApi中使用
computed: {
...mapGetters({
sName: 'nameInfo',
sAgeInfo: 'ageInfo'
})
},
computed: {
...mapGetters(["nameInfo", "ageInfo"]),
},
在CompositionAPI中使用
- 方法一
setup() {
const store = useStore();
const totalPrice = computed(() => store.getters.totalPrice)
return {
totalPrice
};
},
- 方法二
import { computed } from "vue";
import { useStore, mapGetters } from "vuex";
export function useGetter(mappers) {
const store = useStore()
const storeGetterFn = mapGetters(mappers)
const storeGetter = {}
console.log('storeGetterFn', storeGetterFn)
Object.keys(storeGetterFn).forEach(FnKey => {
const fn = storeGetterFn[FnKey].bind({$store: store})
storeGetter[FnKey] = computed(fn)
})
return storeGetter
}
<template>
<div>
<div>home</div>
<hr />
<div>{{ $store.getters.totalPrice }}</div>
<hr />
<div>{{ totalPrice }}</div>
<hr />
<div>{{ totalPriceCountGreaterN(1) }}</div>
<hr />
<br />
<router-view />
</div>
</template>
<script>
import { useGetter } from '@/hooks/useGetters'
export default {
name: "",
setup() {
const storeGetter = useGetter(["totalPrice", "totalPriceCountGreaterN"])
return {
...storeGetter
};
},
};
</script>
state与getters的封装基本一致,只是mapState与mapGetters不同,所以将公共部分进行封装
import { computed } from "vue"
import { useStore } from "vuex"
export function useMapper(mapper, mapFn) {
// 拿到store独享
const store = useStore()
// 获取到对应的对象的functions: {name: function, age: function}
// mapState: 支持数组和对象
const storeStateFn = mapFn(mapper)
// 对数据进行转换
const storeState = {}
Object.keys(storeStateFn).forEach(fnKey => {
console.log(fnKey)
const fn = storeStateFn[fnKey].bind({$store: store})
storeState[fnKey] = computed(fn)
})
return storeState
}
三、mutations
mapMutations辅助函数
- optionsAPI
<button @click="increment_n({ n: 1 })">+1</button>
<br />
<button @click="incrementN">+1</button>
methods: {
...mapMutations([INCREMENT_N]),
incrementN() {
this[INCREMENT_N]({n: 1})
}
}
- CompositionAPI
// import { INCREMENT_N } from "@/store/mutation-types";
setup() {
const mutations1 = mapMutations({
addNum: 'increment'
})
const mutations2 = mapMutations([INCREMENT_N])
return {
...mutations1,
...mutations2
};
},
mutation重要原则
mutation必須是同步函數
- devtool工具会记录mutation的日记
- 每一条mutation被记录,devtools都需要捕捉前一状态和后一状态的快照
- 但是在mutation中执行异步操作,就无法追踪到数据的变化
- 所以Vuex的重要原则中要求mutation必须是
同步函数
四、actions
使用
// store
mutations: {
[INCREMENT_N](state, payload) {
state.counter += payload.n
},
increment(state) {
state.counter += 1
}
},
actions: {
incrementA(context, payload) {
setTimeout(() => {
context.commit('increment')
// context.commit(INCREMENT_N, {n: 10})
}, 1000)
},
decrementAction({commit, dispatch, state, rootState, getters, rootGetters}) {
commit('decrement')
}
}
派发
// 方法一
incrementN() {
this.$store.dispatch('incrementA', {name: 'xxx'})
}
// 方法二
incrementN() {
this.$store.dispatch({
type: 'incrementA'
})
}
辅助函数
// import { mapActions } from "vuex";
...mapActions(['incrementA']),
...mapActions({
add: 'incrementA'
})
setup() {
const actions = mapActions(['incrementA'])
const actions2 = mapActions({
add: 'incrementA'
})
return {
...actions,
...actions2
};
},
监听结束
Fn(context) {
return new Promise((resolve, reject) => {
axios.get('xxx').then(res => {
context.commit('xxx', res)
resolve()
}).catch(err => {
rejetc(err)
})
})
}
onMounted(() => {
const promise = store.dispatch('Fn')
promise.then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
})
modules
使用
// index.js
import { createStore } from 'vuex'
import homeModule from './modules/home'
import userModule from './modules/user'
const store = createStore({
state() {
return {
rootCounter: 0
}
},
modules: {
home: homeModule,
userModule // key-value一致
}
})
export default store
// modules/home
const homeModule = {
state() {
return {
homeCounter: 100
}
},
getters: {},
mutations: {},
actions: {}
}
export default homeModule
// modules/user
const userModule = {
state() {
return {
userCounter: 100
}
},
getters: {},
mutations: {},
actions: {}
}
export default userModule
<template>
<div>
<h2>{{$store.state.rootCounter}}</h2>
<h2>{{$store.state.home.homeCounter}}</h2>
<h2>{{$store.state.userModule.userCounter}}</h2>
// getters做了合并
<h2>{{$store.getters.doubleHomeCounter}}</h2>
</div>
</template>
namespaced
命名空间
namespaced: true
const homeModule = {
namespaced: true,
state() {
return {
homeCounter: 100
}
},
getters: {
// 模块中,getters的参数有 state, getters, rootState, rootGetters
// rootState, rootGetters根里面的状态
doubleHomeCounter(state, getters, rootState, rootGetters) {
return state.homeCounter * 2
}
},
mutations: {
increment(state) {
state.homeCounter++
}
},
actions: {
increment({commit, dispatch, state, rootState, getters, rootGetters}){
context.commit('increment')
// context.commit('increment', payload / null, {root: true}) // root: true表示对根进行提交
// dispatch('incrementAction', null, {root: true})
}
}
}
export default homeModule
<h2>{{$store.getters["home/doubleHomeCounter"}}</h2>
methods: {
homeIncrementAction() {
this.$store.dispatch("home/incrementAction")
}
}
module里面的辅助函数
<template>
<div>
<h2>{{ $store.state.rootCounter }}</h2>
<h2>{{ $store.state.home.homeCounter }}</h2>
<h2>{{ $store.state.userModule.userCounter }}</h2>
<hr />
<h2>{{ homeCounter }}</h2>
<h2>{{ doubleHomeCounter }}</h2>
<button @click="increment">+1</button>
</div>
</template>
<script>
// import { mapState, mapGetters, mapActions, mapMutations } from 'vuex' // 方法1与方法2
import { createNamespacedHelpers } from 'vuex';
// createNamespacedHelpers 帮助工具
const { mapState, mapGetters, mapActions, mapMutations } = createNamespacedHelpers('home')
export default {
computed: {
// 方法1
// ...mapState({
// homeCounter: state => state.home.homeCounter
// }),
// ...mapGetters({
// doubleHomeCounter: 'home/doubleHomeCounter'
// })
// 方法2
// ...mapState('home', ['homeCounter']),
// ...mapGetters('home', ['doubleHomeCounter']),
// 方法3 createNamespacedHelpers
...mapState(['homeCounter']),
...mapGetters(['doubleHomeCounter'])
},
methods: {
// 方法一
// ...mapMutations({
// increment: 'home/increment'
// }),
// ...mapActions({
// incrementActions: 'home/incrementActions'
// })
// 方法2
// ...mapMutations('home', ['increment']),
// ...mapActions('home', ['incrementActions'])
// 方法3 createNamespacedHelpers
...mapMutations(['increment']),
...mapActions(['incrementActions'])
·· }
}
</script>
在setup中
之前已封装了mapState、mapGetters, 但是 并没有考虑到模块的情况
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)
}
createNamespacedHelpers(moduleName) 拿到指定模块的辅助函数
import { mapGetters, createNamespacedHelpers } from "vuex"
import { useMapper } from './useMapper'
export function useState(moduleName, mapper) {
let mapperFn = mapGetters
if (typeof moduleName === 'string' && moduleName.length > 0) {
mapperFn = createNamespacedHelpers(moduleName).mapGetters
} else {
// 如果不传模块名称
mapper = moduleName
}
return useMapper(mapper, mapperFn)
}
import { mapMutations, mapActions } from "vuex";
import { useState, useGetters } from "@/hooks";
export default {
setup() {
const state = useState("home", ["homeCounter"]);
const getter = useGetters("home", ["doubleHomeCounter"]);
const mutations = mapMutations("home", ["increment"]);
const actions = mapActions("home", ["incrementActions"]);
return {
...state,
...getter,
...mutations,
...actions,
};
},
};
学习笔记,codeWhy