赞
踩
在之前的文章中,我们已经对 Vue.js 有了一定的了解。今天我们要对Vue官方的状态共享管理器Vuex进行详细讲解,将其基本吃透,目标是面对大多数业务需求;
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 随着 Vue 一同开发,并且是 Vue.js 官方维护的状态管理库。
在 Vue.js 应用程序中,组件之间的通信是通过 props 和事件来实现的,这种方式在组件层级较浅()的情况下可以很好地管理状态,但是随着应用规模的增长,状态的管理会变得复杂和困难。这时候就需要使用 Vuex 来解决状态管理的问题。
Vuex 中包含以下核心概念:
State
(状态): 即存储在 Vuex 中的数据,它代表了整个应用的状态。Getter
(获取器): 用于从状态树state中派生出一些状态,类似于计算属性Mutation
(变更): 是唯一允许修改 Vuex 中状态的地方,但是必须是同步函数。Action
(动作): 用于提交 mutation,而不是直接变更状态。可以包含任意异步操作。Vuex
适用于中大型的 Vue.js 应用程序,特别是当应用的状态变得复杂且需要在多个组件之间共享时。它提供了一种集中式管理状态的方式,使得状态管理变得更加可预测和可维护。
Vuex既适用于Vue2,也适用于Vue3;
当应用中包含以下情况时,考虑使用 Vuex:
当应用程序的状态变得较简单或者组件之间的状态传递并不频繁时,可能并不需要使用 Vuex。在这种情况下,可以考虑使用 Vue.js 的局部状态(data)来管理组件的状态。
Vuex 的核心包含四个主要部分:State
、Getters
、Mutations
和 Actions
。
State 是 Vuex 存储数据的地方,它类似于组件中的 data。State 中的数据可以通过 this.$store.state 访问。
state: {
message: 'Hello, Vuex!', // 字符串类型变量
count: 0, // 数字类型变量
isActive: false, // 布尔类型变量
user: { // 对象类型变量
name: 'John Doe',
age: 30
},
items: ['apple', 'banana', 'orange'], // 数组类型变量
currentDate: new Date() // 引用类型变量
}
②在 Vuex 中定义模块的 state:
// Module A const moduleA = { state: { message: 'Hello from Module A', count: 0 } }; // Module B const moduleB = { state: { message: 'Hello from Module B', isActive: true } }; // Vuex Store const store = new Vuex.Store({ modules: { moduleA, moduleB } });
在这个示例中,我们定义了两个模块 moduleA
和 moduleB
,每个模块都有自己的 state
对象,包含了不同的状态属性。
以下是两种在组件中使用 Vuex state 的案例:
<template>
<div>
<p>Message: {{ $store.state.message }}</p>
<p>Count: {{ $store.state.count }}</p>
<p>Active: {{ $store.state.isActive }}</p>
<p>User: {{ $store.state.user.name }} ({{ $store.state.user.age }})</p>
<p>Items: {{ $store.state.items }}</p>
<p>Current Date: {{ $store.state.currentDate }}</p>
</div>
</template>
特点:
$store.state
可以在模板中直接使用 Vuex store 中的 state,语法简单直接。$store.state
可能会导致代码冗长,难以维护,尤其是在大型应用中。② 根Vuex使用 mapState 辅助函数:
<template> <div> <p>Message: {{ message }}</p> <p>Count: {{ count }}</p> <p>Active: {{ isActive }}</p> <p>User: {{ user.name }} ({{ user.age }})</p> <p>Items: {{ items }}</p> <p>Current Date: {{ currentDate }}</p> </div> </template> <script> import { mapState } from 'vuex'; export default { computed: { ...mapState([ 'message', 'count', 'isActive', 'user', 'items', 'currentDate' ]) // 传入数组 } }; </script>
特点:
<template>
<div>
<p>Message from Module A: {{ $store.state.moduleA.message }}</p>
<p>Count from Module A: {{ $store.state.moduleA.count }}</p>
<p>Message from Module B: {{ $store.state.moduleB.message }}</p>
<p>isActive from Module B: {{ $store.state.moduleB.isActive }}</p>
</div>
</template>
④模块state
使用 mapState
辅助函数
<template> <div> <p>Message from Module A: {{ moduleA_message }}</p> <p>Count from Module A: {{ moduleA_count }}</p> <p>Message from Module B: {{ moduleB_message }}</p> <p>isActive from Module B: {{ moduleB_isActive }}</p> </div> </template> <script> import { mapState } from 'vuex'; export default { computed: { ...mapState('moduleA', [ // 模块名 'message as moduleA_message', // 使用别名来避免与全局变量冲突 'count as moduleA_count' // count是原名,moduleA_count是别名 ]), ...mapState('moduleB', [ 'message as moduleB_message', 'isActive as moduleB_isActive' ]) } }; </script>
在模板中,你可以直接使用这两个计算属性来获取模块 A 和模块 B 中的 message 状态。下面是模板中如何使用这两个计算属性的示例:
<template> <div> <p>Message from Module A: {{ moduleA_message }}</p> <p>Message from Module B: {{ moduleB_message }}</p> </div> </template> <script> import { mapState } from 'vuex'; export default { computed: { ...mapState({ // 使用命名空间来映射模块中的状态 moduleA_message: state => state.moduleA.message, // 模块A下的message对象,别名为moduleA_message moduleB_message: state => state.moduleB.message // moduleA_message可以用引号括住,也可以不要引号 }) } }; </script>
在这个案例中,我们展示了五种使用 的 state
的方式。基于模块创建的state使用和基于根Vuex的state使用;
Getter
允许我们在Vuex
中派生状态,它类似于组件中的 computed
属性。Getter
可以接收 state
作为参数,然后返回派生出的状态。
const store = new Vuex.Store({
state: {
todos: [
{ id: 1, text: 'Todo 1', done: true },
{ id: 2, text: 'Todo 2', done: false }
]
},
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done);
}
}
});
在 Vuex 中,Getter 可以用来对 store 中的 state 进行一些计算或筛选,从而得到我们想要的状态,而不需要直接修改原始的 state。这样做有几个好处:
在上面的示例中,我们定义了一个名为 doneTodos
的 Getter
,它接收 store
中的 state
作为参数,然后返回一个数组,这个数组包含了所有已完成的 todo
。在组件中可以直接使用 doneTodos
这个 Getter
来获取已完成的 todo
,而无需在组件中手动筛选 todo
数组。这样可以使得组件中的代码更加简洁和清晰。
对于第三点的复用性和组合性,案例如下:
const store = new Vuex.Store({ state: { todos: [ { id: 1, text: 'Todo 1', done: true }, { id: 2, text: 'Todo 2', done: false } ] }, getters: { doneTodos: state => { return state.todos.filter(todo => todo.done); }, undoneTodos: state => { return state.todos.filter(todo => !todo.done); }, totalTodosCount: state => { return state.todos.length; }, undoneTodosCount: (state, getters) => { return getters.doneTodos.length; // undoneTodosCount Getter 使用了 doneTodos Getter 来获取所有已完成的任务 } } });
在 Vuex 中,一个模块的 Getter 可以访问其他模块的 state 和 Getter,以及根模块的 state 和 Getter。这是因为在 Getter 中,可以通过参数访问当前模块的 state 和 Getter,以及 rootState 和 rootGetters。同理Vuex根getters也可以访问其他模块的state和getters;案例代码如下:
const store = new Vuex.Store({ modules: { moduleA: { state: { message: 'Module A Message' }, getters: { moduleA_message: state => state.message } }, moduleB: { state: { message: 'Module B Message' }, getters: { moduleB_message: state => state.message } }, moduleC: { getters: { combinedMessages: (state, getters, rootState, rootGetters) => { const moduleA_message = rootGetters['moduleA/moduleA_message']; const moduleB_message = rootGetters['moduleB/moduleB_message']; return `${moduleA_message} - ${moduleB_message}`; } } } } }); const store = new Vuex.Store({ modules: { moduleA: { state: { message: 'Module A Message' }, getters: { moduleA_message: state => state.message } } }, getters: { moduleA_message: (state, rootGetters) => { return rootGetters['moduleA/moduleA_message']; } } });
从这个角度看,rootGetters
是一个对象,其中每个键代表每个模块名称,值为每个模块对于的getter
;
同理rootState.moduleName.stateName
可以访问不同模块间的state
属性;
Getter
在默认情况下是具有缓存特性的,意味着在相同的状态下多次调用相同的 Getter,只会计算一次,并且在下次调用时直接返回缓存的结果,而不会重新计算。这可以提高性能,避免重复计算。
在 Vuex
中,Getter
是一个函数,可以接收 state
、getters
和 rootState
作为参数。这使得 Getter 具有更大的灵活性, 由于 Getter 是函数,因此可以在其中进行条件判断和计算,根据不同的情况返回不同的值,从而实现更灵活的状态派生和计算逻辑;
Mutations 是 Vuex 中用来唯一能修改状态的函数,类似于组件中的 methods。Mutation 函数接收当前状态 (state) 作为第一个参数,并且可以接收额外的参数作为需要修改的数据。Mutation 必须是同步函数,不能包含异步操作。
const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment(state) { state.count++; }, decrement(state) { state.count--; }, incrementBy(state, payload) { state.count += payload.amount; } } });
methods: {
increment() {
this.$store.commit('increment');
},
decrement() {
this.$store.commit('decrement');
},
incrementBy(amount) {
this.$store.commit('incrementBy', { amount }); // this指的是Vue实例,第二个参数等于{ amount: amount }
}
}
const store = new Vuex.Store({ state: { count: 0, message: 'Hello, Vuex!' }, mutations: { updateState(state, payload) { // payload 是一个包含多个状态的对象 Object.assign(state, payload); } }, actions: { updateState(context, payload) { context.commit('updateState', payload); } } }); // 在组件中调用这个 Mutation 来批量修改状态 this.$store.commit('updateState', { // 如果是模块,则this.$store.commit('moduleName/mutationName', payload) count: 10, message: 'Hello, Vuex! Updated!' });
Object.assign(state, payload);是浅拷贝方法,因此组件中这样使用,可能会导致整个模块或者全局都改变!
Action
类似于 Mutation
,但具有异步操作的能力;它提交的是 Mutation,而不是直接变更状态。其通过 store.dispatch
触发Mutations 来变更状态。
Actions 接收一个上下文对象 (context)
,这个context
是包含了与 Vuex
实例具有相同方法和属性的对象,例如 state
、commit
、dispatch
等;
const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment(state) { state.count++; } }, actions: { incrementAsync(context) { setTimeout(() => { context.commit('increment'); // 变量名为对应mutation名称 }, 1000); } } });
在 Vuex 中,每个模块module内部都有一个上下文对象 (context),它提供了与 根store 实例具有相同方法和属性的对象,包括 state(属性)、commit(方法,提交mutation)、dispatch(方法,分发action触发异步) 等。这样设计的目的是为了让模块内部的 actions、mutations、getters 等能够访问和操作模块的局部状态,而不需要通过全局的 store 对象。
Actions
可以通过 store.dispatch
方法在组件中触发。在 Action
中,可以执行异步操作,例如发起 HTTP 请求、定时器等,然后在异步操作完成后提交 Mutations 来变更状态
// 组件中触发 Action
methods: {
incrementAsync() {
this.$store.dispatch('incrementAsync');
}
}
mapState 函数用于将 Vuex 中的 state 映射为组件的计算属性。它接收一个数组或对象作为参数,数组中可以是 state 中的属性名,也可以是对象形式,对象的键是组件中的属性名,值是 state 中的属性名或者是一个返回状态的函数。这样,在组件中就可以直接使用这些计算属性,无需再通过 $store.state.xxx 的方式访问 Vuex 中的状态。
import { mapState } from 'vuex';
export default {
computed: {
// 使用数组
...mapState(['count', 'message']),
// 使用对象
...mapState({
user: state => state.userInfo,
isLoggedIn: state => state.auth.isLoggedIn
})
}
};
mapGetters
函数用于将 Vuex
中的 getters
映射为组件的计算属性。它接收一个数组或对象作为参数,用法与 mapState
相似,可以使用数组形式或者对象形式来进行映射。
import { mapGetters } from 'vuex';
export default {
computed: {
...mapGetters(['doneTodosCount', 'undoneTodosCount']),
...mapGetters({
formattedMessage: 'formattedMessage'
})
}
};
mapMutations
函数用于将 Vuex
中的 mutations
映射为组件的方法。它接收一个数组或对象作为参数,数组中是 mutations
中的方法名,对象形式的键是组件中的方法名,值是 mutations 中的方法名。
import { mapMutations } from 'vuex';
export default {
methods: {
...mapMutations(['increment', 'decrement']),
...mapMutations({
setUserName: 'SET_USER_NAME'
})
}
};
数组方法直接用,对象方法可以改别名后使用
...mapMutations(['increment', 'decrement'])
等价于以下代码:
{
increment: function() {
return this.$store.commit('increment');
},
decrement: function() {
return this.$store.commit('decrement');
}
}
它将 mapMutations
返回的对象展开,将其中的每个方法映射到组件中,使得用户可以直接调用 this.increment()
和 this.decrement()
来提交对应的 mutations
。
mapActions
函数用于将 Vuex
中的 actions
映射为组件的方法。用法与 mapMutations
类似,接收一个数组或对象作为参数,数组中是 actions 中的方法名,对象形式的键是组件中的方法名,值是 actions 中的方法名。
import { mapActions } from 'vuex';
export default {
methods: {
...mapActions(['login', 'logout']),
...mapActions({
fetchUserData: 'FETCH_USER_DATA'
})
}
};
通常情况下,mutation
用于同步地修改状态,而 actions
则用于包含异步操作的场景。在 actions
中可以调用多个 mutation
来修改状态,但通常不会在 actions 中定义不需要通过 mutation 修改状态的独立方法。这样可以保持状态变更的可追踪性和可维护性。
当在 actions 中执行异步操作时,可能需要在异步操作完成后修改多个状态。例如,假设有一个购物车功能,需要在用户点击结账按钮时执行以下操作:
在这种情况下,可以在 actions
中调用多个 mutation
来修改这些状态。以下是示例代码:
const store = new Vuex.Store({ state: { cart: [ { id: 1, name: 'Product 1', price: 10 }, { id: 2, name: 'Product 2', price: 20 }, // 其他商品 ], orders: [], }, getters: { totalAmount: state => { return state.cart.reduce((total, product) => total + product.price, 0); // 累加,p1为累加器,p2为当前元素,0为初始值 } }, mutations: { clearCart(state) { state.cart = []; }, addToOrders(state, products) { state.orders.push(...products); }, resetTotalAmount(state) { state.totalAmount = 0; } }, actions: { checkout({ commit, state }) { // 模拟异步操作,例如向后端发送订单请求 setTimeout(() => { // 异步操作完成后,调用多个 mutation 来修改状态 commit('addToOrders', state.cart); // 添加到订单 commit('clearCart'); // 清空购物车 commit('resetTotalAmount'); //清空总额 }, 1000); } } });
在上面的示例中,checkout action 执行了一系列异步操作,然后调用了多个 mutation 来修改状态。
在 WebGIS 中,Vuex 可以应用于许多场景,以管理地图状态、图层数据、用户交互等。以下是一些常见的 WebGIS 应用场景:
针对一个中大型WebGIS项目,可以采用以下方法论来设计和组织 Vuex 的解决方案:
在本文中,我们全面地探讨了 Vuex
,这是一个专为 Vue.js 应用程序开发的状态管理模式。通过学习 Vuex 的基础概念、高级用法、辅助函数和应用场景,我们深入理解了 Vuex 的工作原理和应用方法。
通Vuex 提供了一种统一的状态管理方案,使得不同组件之间可以轻松共享状态,并且保证状态的一致性。
在实际项目中,我们可以利用 Vuex 来管理购物车状态、用户认证状态、表单数据状态等。通过合理地设计和使用 Vuex,我们可以有效地管理复杂的状态逻辑,并且提高项目的开发效率和质量。
如果觉得我的文章对您有帮助,三连+关注便是对我创作的最大鼓励!或者一个star
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/231832
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。