Vuex

发布时间 2023-11-29 18:09:13作者: 我是小凳子

Vuex 用于状态管理

  1. 状态管理模式:
  • 状态:驱动应用的数据源。state

  • 视图:以声明方式将状态映射到视图。

  • 操作:响应在仕途上的用户输入导致的状态变化。

  1. Vuex 的状态存储是响应式的。不能直接改变 store 中的状态。
  • 从 store 实例中读取状态最简单的方法就是在 计算属性 中返回。this.$store.state.count
const store = createStore({
  state: {
    count: 0,
    list:[-1, 2, 7, 0]
  },

  getters: {
    getStr(state) {
      return `这是一个数:${state.count}`
    },

    // 返回一个函数:过滤出大于 value 的元素
    filterList: (state) => (value) => {
      return state.list.filter(item => item > value)
    }
  },

  mutations: {
    increment(state) {
      // 变更状态
      state.count++
    }
  },

  actions: {
    increment(context) {
      context.commit('increment')
    }
  }
})

State - mapState 辅助函数

  1. 当一个组件需要获取多个状态时,可以使用 mapState 辅助函数来生成计算属性:
import { mapState } from "vuex"

export default {
  computed: mapState({
    // 1. 箭头函数
    count: state => state.count,

    // 2. 传字符串参数 count 等同于 state => state.count
    countAlias: 'count',

    // 3. 为了能使用 this 获取局部状态,必须使用常规函数
    countPlusLocalState(state) {
      return state.count + this.localCount
    }
  })
}
  1. 当映射的计算属性的名称与 state 的子节点名称相同时:computed: mapState(['count'])

  2. mapState 函数返回的是一个对象

Getter 可以认为是 store 的计算属性

  1. Getter 接受 state 作为第一个参数。可以接受 其他 getter 作为第二个参数。

  2. 访问:

  • 通过属性访问:this.$store.getters.getStr

  • 通过方法访问:store.getters.filterList(0) // [2, 7]

getter 在通过方法访问时,每次都会调用,不会缓存结果。

  1. mapGetters 辅助函数: computed: { ...mapGetters(['count']) }

如果想将一个 getter 属性别名:...mapGetters({ num: 'count' })

Mutation 都是同步事务

  1. 更改 Vuex 的 store 中的状态的唯一方法是提交 mutation 。this.$store.commit('increment')

  2. 每个 mutation 都有一个字符串的事件类型(type)和一个回调函数(handler) , 这个回调函数就是我们实际进行状态更改的地方,它接受 state 作为第一个参数。

  3. 调用

  • 不能直接调用一个 mutation 处理函数。需要以相应的 type 调用 store.commit 方法:store.commit('increment')

  • 提交载荷:可以向 store.commit() 传入额外的参数,即 mutation 的载荷。store.commit('increment', { amount: 10 })

// mutation 的处理:
mutations: {
  increment(state, payload) {
    state.count += payload.amount
  }
}
  • 提交 mutation 的另一种方式是直接使用包含 type 属性的对象:store.commit({ type:'increment', amount }) , 这是将整个对象作为载荷传给 mutation 函数,因此处理函数保持不变。
  1. 使用常量替代 Mutation 事件类型

  2. Mutation 必须是同步函数

  3. mapMutations 辅助函数,支持载荷:

// 在组件中:
methods: {
...mapMutations(['increment']) // 将 this.increment() 映射为 this.$store.commit('increment')

...mapMutations({ add: 'increment' }) // 将 this.add() 映射为 this.$store.commit('increment')
}

Action 可以包含异步操作

类似 Mutation ,不同的是:

  • Action 提交的是 mutation , 不是直接变更状态。

  • Action 可以包含任意异步操作

  1. Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,可以调用 context.commit 、 context.state 和 context.getters 。

  2. 分发 Action :store.dispatch('increment') , 支持载荷方式和对象方式。

  3. 在组件中分发 Action : this.$store.dispatch('increment') 或者使用 mapActions 辅助函数。

  4. store.dispatch() 可以处理被触发的 action 的处理函数返回的 Promise ,并且 store.dispatch 仍旧返回 Promise 。

Module 模块

  1. 不要在不同的、无命名空间的模块中定义两个相同的 getter 从而导致错误。

  2. 可以通过添加 namespaced: true 的方式使其成为带命名空间的模块。getter、action 及 mutation都会自动根据模块注册的路径调整命名。

const store = createStore({
  modules: {
    user: {
      namespaced: true, // 开启命名空间

      getters: {
        isAdmin() {} // -> getters['user/isAdmin']
      }
    }
  }
})
  1. 在带命名空间的模块内访问全局内容:
  • 在全局命名空间内分发 action 或提交 mutation,将 { root: true } 作为第三参数传给 dispatch 或 commit 。

  • 在带命名空间的模块注册全局 action,你可添加 root: true,并将这个 action 的定义放在函数 handler 中。

  • 带命名空间的绑定函数

  • 模块动态注册 store.registerModule()

const store = createStore({/* ... */})

// 注册模块 user
store.registerModule('user', {})
// 访问
store.state.user

// 注册嵌套模块 user/info
store.registerModule(['user','info'], {}) // 注意:传的是数组,不是路径字符串
// 访问
store.state.user.info
  • 动态卸载模块:store.unregisterModule(moduleName) 注意,不能用于卸载静态模块(即创建store时声明的模块)。

  • 检查模块是否已经被注册到 store :store.hasModule(moduleName) 注意,传的是数组,不是路径字符串。

  1. 保留 state : store.registerModule('a', module, { preserveState: true })

当你设置 preserveState: true 时,该模块会被注册,action、mutation 和 getter 会被添加到 store 中,但是 state 不会。

  1. 模块重用

如果需要创建一个模块的多个实例,如果是用纯对象来声明模块的状态,可能会造成污染问题,这时候可以使用一个函数来声明。(同 Vue 组件中的 data 都是一样的问题)

组合式API

import { useStore } from "vuex"

const store = useStore()

表单处理

如果有一种场景,需要在表单上绑定 store.state.user.name <input v-model="user.name"> 且通过输入的值更新它:

  • 方法1:舍弃双向绑定 <input :value="username" @input="() => {}">,通过 @input 事件去 commit mutations 。

  • 方法2:利用 带有setter 的 computed 计算属性来改变值:

computed: {
  username: {
    get() {
      return this.$store.state.user.name
    }

    set(value) {
      this.$store.commit('updateName', value)
    }
  }
}

TypeScript 支持