Vue Getters和mapGetters的原理及使用示例详解
作者:繁依Fanyi
在Vue.js的状态管理中,Vuex是一个非常重要的工具,它帮助开发者集中管理应用的状态。Vuex的核心概念包括state、mutations、actions、getters和modules。今天,我们要深入探讨其中一个关键部分:getters,以及它的相关辅助函数mapGetters。通过详细介绍getters的原理和实现过程,希望能帮助你更好地理解和使用它们。
什么是Vue Getters?
Vuex中的getters可以被视为store的计算属性。就像Vue组件中的计算属性一样,getters的返回值会基于其依赖被缓存起来,且只有当它的依赖值发生变化时才会重新计算。这使得getters非常适合用于从store中的state派生出一些状态。
基本使用
首先,让我们看一个简单的例子:
const store = new Vuex.Store({ state: { todos: [ { id: 1, text: 'Learn Vue', done: true }, { id: 2, text: 'Learn Vuex', done: false } ] }, getters: { doneTodos: state => { return state.todos.filter(todo => todo.done) }, doneTodosCount: (state, getters) => { return getters.doneTodos.length } } })
在上面的代码中,我们定义了一个doneTodos
的getter,它会返回所有已完成的任务。同时,我们还定义了一个doneTodosCount
的getter,它依赖于doneTodos
,返回已完成任务的数量。
访问Getters
你可以通过store.getters
来访问getters:
store.getters.doneTodos // -> [{ id: 1, text: 'Learn Vue', done: true }] store.getters.doneTodosCount // -> 1
在组件中使用Getters
在Vue组件中,你可以使用this.$store.getters
来访问getters:
computed: { doneTodos () { return this.$store.getters.doneTodos } }
这虽然工作正常,但对于多个getters的访问会显得有些冗长。为了解决这个问题,我们可以使用mapGetters
辅助函数。
使用mapGetters
mapGetters
是一个辅助函数,它可以帮助我们将store中的getter映射到局部计算属性。它可以极大地简化在组件中使用getters的代码量。
基本使用
首先,我们需要在组件中导入mapGetters
:
import { mapGetters } from 'vuex' export default { computed: { ...mapGetters([ 'doneTodos', 'doneTodosCount' ]) } }
现在,我们可以直接在模板中使用这些计算属性:
<template> <div> <p>Done Todos: {{ doneTodos }}</p> <p>Done Todos Count: {{ doneTodosCount }}</p> </div> </template>
别名
有时候,我们可能想要为映射的计算属性指定别名。这时可以使用对象形式的参数:
computed: { ...mapGetters({ completedTasks: 'doneTodos', completedTasksCount: 'doneTodosCount' }) }
这样,我们就可以在模板中使用别名:
<template> <div> <p>Completed Tasks: {{ completedTasks }}</p> <p>Completed Tasks Count: {{ completedTasksCount }}</p> </div> </template>
Getters的原理和实现
为了更深入地理解getters的工作原理,我们需要了解Vuex的内部实现。Vuex是基于Vue的响应系统构建的,因此getters的实现与Vue的计算属性有很多相似之处。
创建Getters
当我们创建一个store时,Vuex会遍历我们定义的所有getters,并为每一个getter创建一个计算属性。这些计算属性的结果会被缓存,只有当它们的依赖(即state或者其他getters)发生变化时才会重新计算。
class Store { constructor (options = {}) { // ... const store = this const { getters } = options this.getters = {} Object.keys(getters).forEach(key => { const fn = getters[key] Object.defineProperty(store.getters, key, { get: () => fn(store.state, store.getters) }) }) // ... } }
在上面的代码中,我们可以看到Vuex通过Object.defineProperty
为每一个getter定义了一个属性,这个属性的getter函数会返回计算后的结果。
响应式系统
Vuex的state是响应式的,这意味着当我们改变state中的数据时,所有依赖于这些数据的getters都会自动更新。Vuex通过Vue的Vue.observable
方法将state变成响应式对象。
const state = Vue.observable({ todos: [ { id: 1, text: 'Learn Vue', done: true }, { id: 2, text: 'Learn Vuex', done: false } ] })
这样,当我们改变state中的todos时,所有依赖于todos的getters(例如doneTodos
和doneTodosCount
)都会自动重新计算,并触发相关的视图更新。
深入理解mapGetters
mapGetters
是Vuex提供的一个非常有用的辅助函数,它的实现也相对简单。mapGetters
的主要作用是将store中的getters映射到组件的计算属性。
mapGetters的实现
我们来看看mapGetters
的实现:
export function mapGetters (getters) { const res = {} normalizeMap(getters).forEach(({ key, val }) => { res[key] = function mappedGetter () { return this.$store.getters[val] } }) return res }
在上面的代码中,mapGetters
首先通过normalizeMap
函数将传入的参数规范化为一个数组,然后遍历这个数组,为每一个getter创建一个计算属性。这些计算属性的getter函数会返回this.$store.getters
中的对应值。
使用normalizeMap
normalizeMap
函数的作用是将传入的参数(可以是数组或对象)规范化为一个标准的对象数组:
function normalizeMap (map) { if (!isValidMap(map)) { return [] } return Array.isArray(map) ? map.map(key => ({ key, val: key })) : Object.keys(map).map(key => ({ key, val: map[key] })) }
如果传入的是一个数组,normalizeMap
会将每一个数组元素转化为一个对象,键和值相同;如果传入的是一个对象,normalizeMap
会将每一个键值对转化为一个对象,键和值分别对应原对象的键和值。
Getters和mapGetters的实际应用
在实际项目中,getters和mapGetters可以帮助我们更好地组织和管理应用状态。让我们通过一个稍微复杂的例子来进一步理解它们的实际应用。
例子:Todo应用
假设我们在开发一个Todo应用,这个应用需要展示所有任务、已完成任务、未完成任务以及任务的数量。我们可以通过getters来实现这些功能。
首先,我们定义store的state和getters:
const store = new Vuex.Store({ state: { todos: [ { id: 1, text: 'Learn Vue', done: true }, { id: 2, text: 'Learn Vuex', done: false }, { id: 3, text: 'Build something awesome', done: false } ] }, getters: { allTodos: state => state.todos, doneTodos: state => state.todos.filter(todo => todo.done), undoneTodos: state => state.todos.filter(todo => !todo.done), totalTodosCount: state => state.todos.length, doneTodosCount: (state, getters) => getters.doneTodos.length, undoneTodosCount: (state, getters) => getters.undoneTodos.length } })
然后,在组件中使用这些getters:
import { mapGetters } from 'vuex' export default { computed: { ...mapGetters([ 'allTodos', 'doneTodos', 'undoneTodos', 'totalTodosCount', 'doneTodosCount', 'undoneTodosCount' ]) } }
在模板中展示任务和统计信息:
<template> <div> <h1>Todo List</h1> <p>Total Todos: {{ totalTodosCount }}</p> <p>Done Todos: {{ doneTodosCount }}</p> <p>Undone Todos: {{ undoneTodosCount }}</p> <h2>All Todos</h2> <ul> <li v-for="todo in allTodos" :key="todo.id">{{ todo.text }}</li> </ul> <h2>Done Todos</h2> <ul> <li v-for="todo in doneTodos" :key="todo.id">{{ todo.text }}</li> </ul> <h2>Undone Todos</h2> <ul> <li v-for="todo in undoneTodos" :key="todo.id">{{ todo.text }}</li> </ul> </div> </template>
通过这种方式,我们可以清晰地展示所有任务、已完成任务和未完成任务,以及相关的统计信息。而且,这些数据都是通过getters从state派生出来的,当state中的任务列表发生变化时,视图会自动更新。
优化和最佳实践
在实际开发中,除了正确使用getters和mapGetters,我们还可以采取一些优化和最佳实践来提升代码的可维护性和性能。
避免不必要的计算
虽然getters的结果会被缓存,但在设计getters时仍然要注意避免不必要的计算。例如,如果一个getter依赖于另一个getter,我们应该尽量减少重复计算。
模块化
对于大型应用,我们可以将store拆分成多个模块,每个模块都有自己的state、mutations、actions和getters。这样可以使代码更清晰,更易于管理。
const moduleA = { state: () => ({ todos: [] }), getters: { doneTodos: state => state.todos.filter(todo => todo.done) }, mutations: { // ... }, actions: { // ... } } const store = new Vuex.Store({ modules: { a: moduleA } })
在组件中使用模块的getters:
computed: { ...mapGetters('a', [ 'doneTodos' ]) }
异步操作
虽然getters不应该包含异步操作,但我们可以在actions中进行异步操作,然后通过mutations更新state,从而触发getters的重新计算。
const store = new Vuex.Store({ state: { todos: [] }, getters: { doneTodos: state => state.todos.filter(todo => todo.done) }, mutations: { setTodos (state, todos) { state.todos = todos } }, actions: { fetchTodos ({ commit }) { // 假设我们有一个API调用来获取todos fetchTodosFromAPI().then(todos => { commit('setTodos', todos) }) } } })
性能优化
在高性能需求的应用中,我们可以利用Vuex的插件系统来优化getters的性能。例如,我们可以编写一个插件来对getters的结果进行缓存,从而避免频繁的计算。
function createGettersCachePlugin () { return store => { const cache = {} store.subscribe((mutation, state) => { // 在每次mutation后清除缓存 Object.keys(cache).forEach(key => delete cache[key]) }) store._wrappedGetters = Object.keys(store._wrappedGetters).reduce((wrappedGetters, key) => { const getter = store._wrappedGetters[key] wrappedGetters[key] = (state, getters) => { if (!cache[key]) { cache[key] = getter(state, getters) } return cache[key] } return wrappedGetters }, {}) } } const store = new Vuex.Store({ // ... plugins: [createGettersCachePlugin()] })
这个插件在每次mutation后清除缓存,并对getters的结果进行缓存,从而减少不必要的计算。
总结
Vuex的getters和mapGetters是非常强大的工具,它们可以帮助我们从store中的state派生出新的状态,并在组件中方便地使用这些状态。在实际开发中,我们可以通过合理使用getters和mapGetters,提高代码的可维护性和性能。同时,我们还可以采用一些优化和最佳实践,使我们的应用更加健壮和高效。
希望通过本文的详细介绍,你能够对Vuex的getters和mapGetters有更深入的理解,并在实际项目中更好地应用它们。祝你在Vue.js的世界中编程愉快!
到此这篇关于Vue Getters和mapGetters的原理及使用示例详解的文章就介绍到这了,更多相关Vue Getters和mapGetters使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
您可能感兴趣的文章:
- Vue3使用Vuex之mapState与mapGetters详解
- 记一次vuex的mapGetters无效原因及解决
- vuex中...mapstate和...mapgetters的区别及说明
- vue3.0使用mapState,mapGetters和mapActions的方式
- vuex 中辅助函数mapGetters的基本用法详解
- vuex2中使用mapGetters/mapActions报错的解决方法
- vuex中的 mapState,mapGetters,mapActions,mapMutations 的使用
- 详解vuex中mapState,mapGetters,mapMutations,mapActions的作用