编辑
2023-09-04
Vue
00
请注意,本文编写于 171 天前,最后修改于 171 天前,其中某些信息可能已经过时。

目录

安装
直接下载/CDN引用
npm
yarn
自己构建
基本用法
创建store实例
使用mapState
使用mapMutations
使用mapActions
使用mapGetters
进阶用法
常见问题和解决方案
如何在组件外部访问 store
如何在mutation中执行异步操作
如何处理多个action的组合
如何处理表单的双向绑定
总结

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它可以让我们更方便地管理应用程序的数据流,实现组件之间的通信,以及构建复杂的业务逻辑。在本文中,我将以 Vuex 的使用详解为主题,介绍 Vuex 的基本用法和高级用法,以及一些常见的问题和解决方案。本文假设你已经对 Vue.js 有一定的了解。

安装

直接下载/CDN引用

https://unpkg.com/vuex@4

Unpkg.com 提供了基于 npm 的 CDN 链接。以上的链接会一直指向 npm 上发布的最新版本。您也可以通过 https://unpkg.com/vuex@4.0.0/dist/vuex.global.js 这样的方式指定特定的版本。

npm

shell
npm install vuex@next --save

yarn

shell
yarn add vuex@next --save

自己构建

如果需要使用 dev 分支下的最新版本,您可以直接从 GitHub 上克隆代码并自己构建。

shell
git clone https://github.com/vuejs/vuex.git node_modules/vuex cd node_modules/vuex yarn yarn build

基本用法

Vuex 的核心概念是statemutationactiongetter

  • state是存储在 Vuex 中的数据,它是响应式的,也就是说当状态发生变化时,依赖于状态的组件也会自动更新。

  • mutation是唯一能够改变状态的方法,它是同步的,并且每次突变都会被记录下来,方便调试和追踪。

  • action是异步的操作,它可以包含任意的逻辑和副作用,并且通过提交突变来改变状态。

  • getter是对状态的派生或计算属性,它可以让我们更方便地获取和处理状态。

创建store实例

要使用 Vuex,我们首先需要创建一个存储器(store)实例,并将其注册到根组件中:

js
// 创建一个 store 实例 const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment(state) { state.count++ } }, actions: { incrementAsync({ commit }) { setTimeout(() => { commit('increment') }, 1000) } }, getters: { doubleCount(state) { return state.count * 2 } } }) new Vue({ el: '#app', store, // ... })

注册了 store 之后,我们就可以在组件中通过 this.$store 访问 store 实例,并通过以下几种方式使用 Vuex

使用mapState

使用 mapState 辅助函数来将状态映射到组件的计算属性中:

js
import { mapState } from 'vuex' export default { computed: { ...mapState({ count: state => state.count, countAlias: 'count', countPlusLocalState (state) { return state.count + this.localCount } }) } }

使用mapMutations

使用 mapMutations 辅助函数来将突变映射到组件的方法中:

js
import { mapMutations } from 'vuex' export default { methods: { ...mapMutations([ 'increment', 'incrementBy' ]), ...mapMutations({ add: 'increment' }) } }

使用mapActions

使用 mapActions 辅助函数来将动作映射到组件的方法中:

js
import { mapActions } from 'vuex' export default { methods: { ...mapActions([ 'increment', 'incrementBy' ]), ...mapActions({ add: 'increment' }) } }

使用mapGetters

使用 mapGetters 辅助函数来将获取器映射到组件的计算属性中:

js
import { mapGetters } from 'vuex' export default { computed: { ...mapGetters([ 'doubleCount', 'anotherGetter' ]) } }

进阶用法

Vuex 还提供了一些高级用法,让我们可以更灵活和高效地使用 Vuex。这些高级用法包括:

  • 模块module):Vuex 允许我们将 store 分割成多个模块,每个模块拥有自己的statemutationactiongetter。这样可以让我们更好地组织和管理复杂的状态。例如:
js
// 创建一个模块 a const moduleA = { state: () => ({ count: 0 }), mutations: { increment (state) { state.count++ } }, actions: { incrementIfOdd ({ state, commit }) { if (state.count % 2 === 1) { commit('increment') } } }, getters: { doubleCount (state) { return state.count * 2 } } } // 创建一个模块 b const moduleB = { state: () => ({ name: 'Alice' }), mutations: { changeName (state, newName) { state.name = newName } }, actions: { changeNameAsync ({ commit }, newName) { setTimeout(() => { commit('changeName', newName) }, 1000) } }, getters: { nameLength (state) { return state.name.length } } }

然后,我们可以在创建 store 实例时,通过 modules 选项来注册模块:

js
// 创建一个 store 实例 const store = new Vuex.Store({ modules: { a: moduleA, b: moduleB } })

这样,我们就可以通过 store.state.astore.state.b 来访问模块 a 和模块 b 的状态。

同样,我们也可以通过 store.commit('a/increment')store.commit('b/changeName', 'Bob') 来提交模块 a 和模块 bmutationactiongetter也是类似的,只需要在前面加上模块的命名空间即可。

如果我们想要让模块的动作和获取器不受命名空间的限制,我们可以在注册模块时,通过 namespaced: false 来关闭命名空间。

这样,我们就可以直接使用 store.dispatch('incrementIfOdd')store.getters['doubleCount'] 来调用模块 aactiongetter

但是,这样做可能会导致命名冲突的问题,所以建议使用命名空间来避免混淆。

Vuex 还提供了一些辅助函数来简化模块化的使用。例如:

  • 使用 createNamespacedHelpers 函数来创建一个带有命名空间的辅助函数集合:
js
import { createNamespacedHelpers } from 'vuex' const { mapState, mapActions, mapGetters, mapMutations } = createNamespacedHelpers('some/nested/module') export default { computed: { // 在 `some/nested/module` 中查找 ...mapState({ a: state => state.a }), ...mapGetters({ b: 'b' }) }, methods: { // 在 `some/nested/module` 中查找 ...mapActions([ 'foo' ]), ...mapMutations({ bar: 'bar' }) } }
  • 使用 createLogger 插件来打印每次突变的日志:
js
import createLogger from 'vuex/dist/logger' const store = new Vuex.Store({ plugins: [createLogger()] })
  • 使用 createPersistedState 插件来将状态持久化到本地存储中:
js
import createPersistedState from 'vuex-persistedstate' const store = new Vuex.Store({ plugins: [createPersistedState()] })

常见问题和解决方案

Vuex 的使用可能会遇到一些常见的问题和困难,这里列举了一些常见问题和解决方案,希望对你有所帮助。

如何在组件外部访问 store

有时候,我们可能需要在组件外部访问 store,例如在路由钩子函数或者 API 请求中。这时候,我们可以通过导出 store 实例,并在需要的地方导入它来实现。例如:

js
// store.js import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) export const store = new Vuex.Store({ // ... }) // router.js import { store } from './store' router.beforeEach((to, from, next) => { // 访问 store if (store.state.user) { next() } else { next('/login') } }) // api.js import { store } from './store' import axios from 'axios' export function getUserInfo () { // 访问 store const token = store.state.token return axios.get('/user/info', { headers: { Authorization: `Bearer ${token}` } }) }

如何在mutation中执行异步操作

Vuexmutation是同步的,也就是说,我们不能在mutation中执行异步操作,例如 setTimeoutfetchaxios 等。

如果我们需要执行异步操作,我们应该在action中执行,并在异步操作完成后,提交mutation来改变state。例如:

js
// 错误的做法:在突变中执行异步操作 mutations: { incrementAsync (state) { setTimeout(() => { state.count++ }, 1000) } } // 正确的做法:在动作中执行异步操作,并提交突变 actions: { incrementAsync ({ commit }) { setTimeout(() => { commit('increment') }, 1000) } }

如何处理多个action的组合

有时候,我们可能需要将多个action组合起来,形成一个复杂的业务逻辑。这时候,我们可以使用 Promise 或者 async/await 来处理异步流程。例如:

js
actions: { // 使用 Promise actionA ({ commit }) { return new Promise((resolve, reject) => { // 异步操作 setTimeout(() => { commit('someMutation') resolve() }, 1000) }) }, // 使用 async/await async actionB ({ dispatch, commit }) { await dispatch('actionA') // 等待 actionA 完成 commit('anotherMutation') } }

如何处理表单的双向绑定

Vuex 的状态是响应式的,也就是说,当state发生变化时,依赖于state的组件也会自动更新。

但是,这并不意味着我们可以直接在组件中修改state,因为这样会破坏 Vuex 的状态管理模式。

如果我们想要实现表单的双向绑定,我们有以下几种方法:

  • 使用 v-model 的修饰符 .lazy,并在 inputchange 事件中提交mutation
html
<input v-model.lazy="message" @change="updateMessage">
js
computed: { message: { get () { return this.$store.state.message }, set (value) { this.updateMessage(value) } } }, methods: { updateMessage (value) { this.$store.commit('updateMessage', value) } }
  • 使用 v-model 的修饰符 .number.trim,并在 inputchange 事件中提交mutation
html
<input v-model.number="age" type="number" @input="updateAge"> <input v-model.trim="name" @input="updateName">
js
computed: { age: { get () { return this.$store.state.age }, set (value) { this.updateAge(value) } }, name: { get () { return this.$store.state.name }, set (value) { this.updateName(value) } } }, methods: { updateAge (value) { this.$store.commit('updateAge', value) }, updateName (value) { this.$store.commit('updateName', value) } }
  • 使用 computed 属性的 getset 方法,并在 set 方法中提交mutation
html
<input v-model="message">
js
computed: { message: { get () { return this.$store.state.message }, set (value) { this.$store.commit('updateMessage', value) } } }

总结

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它可以让我们更方便地管理应用程序的数据流,实现组件之间的通信,以及构建复杂的业务逻辑。

Vuex 的核心概念是statemutationactiongettermoduleVuex 还提供了一些进阶用法,让我们可以更灵活和高效地使用 VuexVuex 的使用可能会遇到一些常见的问题和困难,文中列举了一些常见问题和解决方案。

如果对你有用的话,可以打赏哦
打赏
ali pay
wechat pay

本文作者:CreatorRay

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!