在Vue中实现集中式状态(数据)管理的一个Vue插件,对vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信。
- 点击查看安装步骤
- 每一个 Vuex应用的核心就是 store(仓库)
- 修改store值的唯一方法就是通过mutation来修改
- actions不能直接修改state需要通过mutations提交修改state,actions可以进行异步操作
状态管理模式
- 状态:驱动应用的数据源 — data
- 视图:以声明方式将状态映射到视图 — template
- 操作: 响应在视图上的用户输入导致的状态变化 — methods
不使用单向数据流的原因:
- 使用场景:多个组件共享状态 – 会依赖同一状态和变更同一状态(单向数据流的简洁性会被破坏)
Vuex 和单纯的全局对象的不同:
- Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。
- 改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。
创建一个简单的store(vue2版)
- 创建文件:
src/store/index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const actions = { jiaWait(context,value){ setTimeout(()=>{ context.commit('increment',value) },500) } }
const mutations = { increment(state,value){ state.count += value }, }
const state = { count: 0 }
const getters = { bigSum(state){ return state.count*10 } }
export default new Vuex.Store({ actions, mutations, state, getters })
|
- 在
main.js
中引入store,并将store
添加到new Vue中
基本使用
- 初始化数据、配置actions、配置mutations,操作文件store.js
- 读取vuex中的数据:页面通过store.state来获取状态对象,组件通过this.$store老访问store实例
- 修改vuex中的数据:通过
this.$store.dispatch('action中的方法名',数据)
或 this.$store.commit('mutations中的方法名',数据)
- 注:若没有网络请求或其他业务逻辑,组件中也可以越过actions,即不写dispatch,直接编写commit
state — 单一状态树
- 组件中获得 Vuex 状态 – 在computed中返回某个state
Getter — store 的计算属性
- 概念:当state中的数据需要经过加工后再使用时,可以使用getters加工。
- 组件中读取数据:
$store.getters.count
Mutation — 更改state的唯一方法 且必须是同步函数
- 提交的两种方式(传入额外的参数)
- 载荷(Payload)
- store.commit(‘mutation的方法名’, {额外参数: n})
- 对象风格
- store.commit({type: ‘mutation的方法名’,额外参数: n})
Action — 包含异步操作且提交的是Mutation
1 2 3 4
| actions: { increment({commit}) { commit('mutations的方法名') } }
|
- 载荷形式分发
- store.dispatch(‘actions的方法名’, {额外参数: n})
- 以对象形式分发
- store.commit({type: ‘actions的方法名’, 额外参数: n})
组合Action
- store.dispatch可以处理触发的action的处理函数返回Promise
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| actions: { actionA({ commit }) { return new Promise((resolve,reject) => { setTimeout(() => { commit('someMutation') resolve() },1000) }) } } store.dispatch('actionA').then(()=>{})
actions: { async actionA({commit}) { commit('gotData', await getData()) }, async actionB ({ dispatch, commit}) { await dispatch('actionA') commit('getOtherData',await getOtherData) } }
|
map方法的使用
mapState方法:映射state中的数据为计算属性
- 使用:在computed中将state的数据拿出来
- 对象写法:…mapState({再定义的变量名:’变量名’})
- 数组写法:…mapState([‘变量名’])
mapGetters方法:映射getters中的数据为计算属性
- 使用:在computed中将getters的方法拿出来
- 对象写法:…mapGetters({再定义的方法名:’方法名’})
- 数组写法:…mapGetters([‘方法名’])
mapActions方法:
- 生成与actions对话的方法,即:包含
$store.dispatch(xxx)
的函数
- 使用:在computed中将actions的方法拿出来
- 对象写法:…mapActions({再定义的方法名:’方法名’})
- 数组写法:…mapActions([‘方法名’])
mapMutations方法:
- 生成与mutations对话的方法,即:包含
$store.commit(xxx)
的函数
- 使用:在computed中将mutations的方法拿出来
- 对象写法:…mapMutations({再定义的方法名:’方法名’})
- 数组写法:…mapMutations([‘方法名’])
- 注:mapActions与mapMutations使用时,若需要传递参数需要:在模板中绑定事件时传递好参数,否则参数是事件对象。
Module
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| const moduleA = { state: () => ({...}), mutations: {...}, actions: {...}, getters: {...}, } const moduleB = { state: () => ({...}), mutations: {...}, actions: {...}, getters: {...} } const store = createStore({ modules: { a: moduleA, b: moduleB } })
store.state.a store.state.b
|
模块化+命名空间
目的:让代码更好维护,让多种数据分类更加明确。
修改store.js
1 2 3 4 5 6 7
| modules: { 模块1: { namespaced: true, state: () => ({...}), getters:{...} } }
|
开启命名空间后,组件中读取state数据:
- 方式一:自己直接读取
- this.$store.state.xxx
- 方式二:借助mapState读取:
- …mapState(‘模块名’,[‘state的属性’])
开启命名空间后,组件中读取getters数据:
- 方式一:自己直接读取
- this.$store.getters[‘模块名/getters的方法’]
- 方式二:借助mapGetters读取:
- …mapGetters(‘模块名’,[‘getters的方法’])
开启命名空间后,组件中调用dispatch
- 方式一:自己直接dispatch
- this.$store.dispatch(‘模块名/actions的方法’,传参)
- 方式二:借助mapActions:
- …mapActions(‘模块名’,{定义的别名:’actions的方法’,定义的别名:’actions的方法’})
开启命名空间后,组件中调用commit
- 方式一:自己直接commit
- this.$store.commit(‘模块名/mutations的方法’,传参)
- 方式二:借助mapMutations:
- …mapMutations(‘模块名’,{别名:’mutations的方法’,别名:’mutations的方法’}),
在带命名空间的模块内访问
全局内容
- 使用全局 state 和 getter,rootState 和 rootGetters 会作为第三和第四参数传入 getter,也会通过 context 对象的属性传入 action。
- 在
全局命名空间
内分发 action 或提交 mutation,将 { root: true } 作为第三参数传给 dispatch 或 commit 即可。
在带命名空间的模块注册
全局 action
- 在命名空间的actions方法中给要注册为全局的方法
添加root: true
和handler (namespacedContext, payload) { ... }
模块动态注册 store.registerModule
1 2 3 4 5 6
| import { createStore } from 'vuex' const store = createStore({}) store.registerModule('myModule',{}) store.registerModule(['nested','myModule'],{}) store.unregisterModule(modulename) store.hasModule(moduleName)
|
- 保留 state
- store.registerModule(‘a’,module, {preserveState: true})
vue3的vuex
- 与vue2比,引入时需要从vuex导入createStore
1 2 3 4 5 6 7
| import { createStore } from 'vuex' export default createStore({ state: {}, mutations: {}, actions: {}, modules: {} })
|
- 使用,在setup中:const store = useStore();然后直接store.xxx即可
组合式API
- 调用useStore,在setup中访问store == this.$store
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import { useStore } from 'vuex' export default { setup () { const store = useStore() return { count: computed(() => store.state.count), double: computed(() => store.getters.double) increment: () => store.commit('increment'), asyncIncrement: () => store.dispatch('asyncIncrement') } } }
|
插件
1 2 3 4 5 6 7 8 9 10
| const myPlugin = (store) => { store.subscribe((mutation,state) => { }) } const store = createStore({ plugin: [myPlugin] })
|
严格模式
- 在store中strict:process.env.NODE_ENV !== ‘production’ // 发布环境不能为true
表单处理
方法一:v-model 用v-bind绑定value和v-on去监听实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <input :value="message" @input="updateMessage" /> computed: { ...mapState({ message: state => state.message }) }, methods: { updateMessage (e) { this.$store.commit('updateMessage',e.detail.value) } } const store = createStore ({ mutations: { updateMessage (state,message) { store.message = message } } })
|
方法二: 用getter和setter的双向绑定计算属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <input v-model="message" /> computed: { message: { get() { return this.$store.state.message }, set (value) { this.$store.commit('updateMessage', value) } } } const store = createStore ({ mutations: { updateMessage (state,message) { store.message = message } } })
|
支持ts
在vuex.d.ts中添加
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import { Store } from 'vuex'
declare module '@vue/runtime-core' { interface State { count: number }
interface ComponentCustomProperties { $store: Store<State> } }
|
useState 组合式函数类型声明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| import { InjectionKey } from 'vue' import { createStore, useStore as baseUseStore, Store } from 'vuex'
export interface State { count: number }
export const key: InjectionKey<Store<State>> = Symbol()
export const store = createStore<State>({ state: { count: 0 } })
export function useStore () { return baseUseStore(key) }
|