小白学前端之TypeScript使用Vuex 4.0
简介
官方介绍:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
简单来说,Vuex 就像是前端的数据库或缓存,不管什么页面,只要 Vuex 里面有的数据,都可以去拿。
Vuex 分为 5 个部分:
- State:是数据源,存放数据
- Getters:可以取得 State 的数据,然后自定义组装返回新的数据
- Mutations:可以改变 State 的数据,建议方法执行是同步的
- Actions:可以异步执行 Mutations 里的方法
- Modules:每个 Module 都有各自的 State、Getters、Mutations、Actions
这 5 个部分相辅相成。
TypeScript 使用
在 vue 项目根目录执行命令来进行 vuex 模块的安装
npm install vuex@next --save
安装好后我们新建文件 /src/store/store.ts
,然后在里面定义 InjectionKey
和 Store
import { InjectionKey } from 'vue'
import { createStore, useStore as baseUseStore, Store } from 'vuex'
// 定义 State 数据类型的接口
interface IState{
}
// 类型传递
export const key: InjectionKey<Store<IState>> = Symbol()
export const store = createStore<IState> ({
})
// 用于组合式API setup() 里,省的每次都传入 key
export function useStore() {
return baseUseStore(key)
}
然后在 main.ts
文件里使用上面定义的 vuex
import { createApp } from 'vue'
import App from './App.vue'
import { store,key } from './store/store'
createApp(App)
.use(store,key)
.mount('#app')
State
State 是存储数据源的地方,所以我们可以在这里存储我们的数据,比如我这边定义一个 name
字段,需要在接口 IState
添加定义数据类型
interface IState{
name: string
}
然后在 createStore
里添加数据
export const store = createStore<IState> ({
state:{
name: 'ooooooh灰灰'
}
})
数据我们已经定义好了,接下来就是要在页面访问这个数据了,下面提供了两种方式来访问 vuex 里的数据
组合式 API 访问
在组合式 API 中,我们可以直接导入刚才在 /src/store/store.ts
里定义的 useStore()
方法来访问 vuex 里的数据
import { defineComponent } from 'vue';
import { useStore } from './store/store'
export default defineComponent({
setup(){
let store = useStore()
// 访问 state 里的 name 数据
console.log(store.state.name)
}
});
运行代码的话就会在控制台打印 ooooooh灰灰
…toRefs() 访问所有字段
如果要在页面访问的话,可以利用 ...toRefs()
来直接展开 store.state 里的所有字段,然后在页面直接访问 vuex 的 state 里的字段
// App.vue
<template>
<div>
{{ name }}
</div>
</template>
<script lang="ts">
import { defineComponent, toRefs} from 'vue';
import { useStore } from './store/store'
export default defineComponent({
setup(){
let store = useStore()
return {
// 展开 state 所有的字段
...toRefs(store.state)
}
}
});
</script>
<style>
</style>
reactive 聚合单个字段
如果你想单个数据导入的话,可以直接和页面数据一起放在 reactive
import { defineComponent, reactive, toRefs} from 'vue';
import { useStore } from './store/store'
export default defineComponent({
setup(){
let store = useStore()
// 把 vuex 的 state 的数据放进 reactive 里
let params = reactive({
name: store.state.name
})
return {
...toRefs(params),
}
}
});
computed 访问单个字段
也可以使用 computed
模块来访问数据,要先导入 vue 里的 computed
// App.vue
<template>
<div>
{{ name }}
</div>
</template>
<script lang="ts">
import { defineComponent, computed} from 'vue';
import { useStore } from './store/store'
export default defineComponent({
setup(){
let store = useStore()
return {
name: computed(()=>store.state.name)
}
}
});
</script>
<style>
</style>
Getters
getters 里的方法在 vuex/types/index.d.ts
中是这样定义的
export type Getter<S, R> = (state: S, getters: any, rootState: R, rootGetters: any) => any;
他有 4 个参数,分别是 state、getters、rootState、rootGetters
其中,state 可以取得同级中 state 里的数据,getters 可以取得同级中 getters 其他的方法返回的数据
而 rootState 和 rootGetters 是在当当前 Getters 处于 module 中时,可以取得根部的 state 和 gatters 里的数据
比如我们可以将 state 里的变量封装成一句话然后返回:
export const store = createStore<IState> ({
state:{
name: 'ooooooh灰灰',
},
getters:{
newName(state):string{
// 通过 state 访问 name 字段
return '大家好!我是:'+state.name
}
}
})
当我们要访问其他 getter 时,我们可以这样:
export const store = createStore<IState> ({
state:{
name: 'ooooooh灰灰',
age: 20
},
getters:{
hello(state,getters):string{
// 通过 getters 访问其他 getter
return '大家好!我是:'+state.name+','+getters.ageInfo
},
ageInfo(state):string{
return '年龄:'+state.age
}
}
})
组合式 API 访问
我们可以在组合式 API 里像访问 state 的里数据一样访问 gatters 里的方法:
import { defineComponent } from 'vue';
import { useStore } from './store/store'
export default defineComponent({
setup(){
let store = useStore()
// 访问 getters 里的 hello 方法
console.log(store.getters.hello)
}
});
此外,getters 也可以使用 ...toRefs()
、computed
这些方法来访问:
<template>
<div>
{{ hello }}
</div>
</template>
<script lang="ts">
import { defineComponent, computed, toRefs } from 'vue';
import { useStore } from './store/store'
export default defineComponent({
setup(){
let store = useStore()
return {
// 通过 computed 访问 getters 里的 hello
hello: computed(()=>store.getters.hello),
// 通过 ...toRefs() 访问
// ...toRefs(store.getters),
}
}
});
</script>
<style>
</style>
Mutations
如果你要改变 state 里的数据时,就要用到 Mutations 了,它可以提供改变 state 里数据的方法,它在 vuex/types/index.d.ts
中是这样定义的:
export type Mutation<S> = (state: S, payload?: any) => any;
其中 state 可以拿到 state 里的数据,payload 是自定义传入的参数,后面有个问号,代表这是可选项
所以当我们要改变 state 的字段的值时,我们可以在 store.ts 中这样写代码 :
export const store = createStore<IState> ({
state:{
name: 'ooooooh灰灰',
},
mutations:{
changeName(state){
// 改变 state 中 name 的值
state.name = 'greycode'
}
}
})
如果要自定义传入参数的话,就可以这样写:
export const store = createStore<IState> ({
state:{
name: 'ooooooh灰灰',
},
mutations:{
changeName(state,newName:string){
// 传入自定义字段并设置
state.name = newName
}
}
})
组合式 API 访问
在组合式 API 中,我们可以用 commit
来提交执行这个方法:
import { defineComponent } from 'vue';
import { useStore } from './store/store'
export default defineComponent({
setup(){
let store = useStore()
let change = () => {
// 提交执行 mutations 中 changeName 方法
// store.commit('changeName')
// 提交执行 mutations 中 changeName 方法,并传入自定义参数
store.commit('changeName','自定义的')
}
return {
change
}
}
});
…mapMutations
我们可以直接在组合式 API 中使用 ...mapMutations
来获得 mutations 中的方法,然后直接在页面中调用这个方法
import { defineComponent } from 'vue';
import { mapMutations } from 'vuex';
import { useStore } from './store/store'
export default defineComponent({
setup(){
let store = useStore()
return {
// 使用 ...mapMutations 来获得 mutations 中的方法
...mapMutations(['changeName'])
}
}
});
然后直接在页面中使用:
<template>
<div>
<button type="button" @click="changeName">按钮</button>
<!-- 也可以传入函数自定义参数 -->
<button type="button" @click="changeName(’自定义名字‘)">按钮</button>
</div>
</template>
Action
当要异步改变 state 中的数据时,就要用到 Action 了,但是它不是直接改变 state 中的数据,而是通过异步执行 mutations 中的方法来间接改变 state 中的数据的
它在 vuex/types/index.d.ts
中是这样定义的:
export type Action<S, R> = ActionHandler<S, R> | ActionObject<S, R>;
它支持两种类型的数据,一个是 ActionHandler<S, R> ,另一个是 ActionObject<S, R>。其中 ActionObject 一般用于 Module 中的命名空间,它们的定义如下:
export type ActionHandler<S, R> = (this: Store<R>, injectee: ActionContext<S, R>, payload?: any) => any;
export interface ActionObject<S, R> {
root?: boolean;
handler: ActionHandler<S, R>;
}
这里只讲下 ActionHandler ,另外一个等到 Module 模块中再讲。
在 ActionHandler 中,它有 3 个参数,分别是 this、injectee、payload,其中 this 代表的是整个 Store 对象,injectee 是当前 Action 所在的上下文,payload 是可以自定义的传入参数
所以我们可以这样使用它:
export const store = createStore<IState> ({
state:{
name: 'ooooooh灰灰'
},
mutations:{
changeName(state){
state.name = '异步改名'
}
},
actions:{
asyncChange(ctx){
// 两秒后更改名字
setTimeout(() =>{
ctx.commit('changeName')
},2000)
}
}
})
组合式 API 访问
定义好 actions 后,我们可以在组合式 API 中用 dispatch
来分发 action:
import { defineComponent } from 'vue';
import { useStore } from './store/store'
export default defineComponent({
setup(){
let store = useStore()
let syncChange = () => {
// 执行 actions 中的 asyncChange 方法
store.dispatch('asyncChange')
}
return {
syncChange
}
}
});
…mapActions
也可以用 ...mapActions
来直接获得 actions 中的方法:
import { defineComponent } from 'vue';
import { mapActions } from 'vuex';
import { useStore } from './store/store'
export default defineComponent({
setup(){
let store = useStore()
return {
...mapActions(['asyncChange'])
}
}
});
页面使用的话和 mutation 差不多,直接访问 actions 中的方法名就可以了:
<template>
<div>
<button type="button" @click="asyncChange">按钮</button>
</div>
</template>
最后
除此之外还有一个 Module 模块,不过一般小项目用不到而且内容也比较多,下次再学吧。