后端程序猿自学前端笔记
简介
官方介绍: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 模块,不过一般小项目用不到而且内容也比较多,下次再学吧。