Vuex源码阅读(二):store内的getters实现逻辑

在Vuex中,getters类似于计算属性(computed),用于派生状态。它们提供了一种便捷的方式来获取经过计算或过滤后的状态数据,并且可以在多个组件之间共享。理解Vuex store内getters的实现逻辑,有助于我们深入掌握Vuex的工作原理,从而更好地在项目中使用它。本文将详细解读Vuex源码中getters的实现逻辑。

目录#

  1. getters的基本概念和使用场景
  2. Vuex中getters的实现原理概述
  3. 源码详细解析
  4. 常见实践和最佳实践
  5. 示例用法
  6. 总结
  7. 参考资料

getters的基本概念和使用场景#

基本概念#

getters是Vuex store中的一个对象,它包含了多个函数,这些函数用于根据store中的状态派生新的数据。每个getter函数接收一个state作为第一个参数,还可以接收其他getter函数作为第二个参数。

使用场景#

  • 数据过滤:当我们需要从store的状态中过滤出特定的数据时,可以使用getters。例如,从一个包含多个商品的列表中过滤出价格低于某个值的商品。
  • 数据计算:对store中的状态进行计算,如计算购物车中商品的总价格。
  • 组件间数据共享:多个组件需要使用相同的派生数据时,使用getters可以避免重复的计算逻辑。

Vuex中getters的实现原理概述#

Vuex中的getters实现原理主要基于Object.defineProperty方法来实现数据的劫持和响应式更新。在初始化store时,会对传入的getters对象进行处理,将每个getter函数封装成一个具有缓存功能的只读属性,并将其挂载到store.getters对象上。当组件访问store.getters中的某个属性时,会触发相应的getter函数进行计算。

源码详细解析#

初始化getters#

在Vuex的Store类的构造函数中,有对getters的初始化逻辑。以下是简化后的代码:

class Store {
  constructor(options = {}) {
    const { getters = {} } = options;
    this.getters = {};
    const computed = {};
 
    // 遍历getters对象
    forEachValue(getters, (fn, key) => {
      // 将getter函数封装到computed对象中
      computed[key] = () => fn(this.state, this.getters);
      // 将getter属性定义到store.getters对象上
      Object.defineProperty(this.getters, key, {
        get: () => this._vm[key],
        enumerable: true
      });
    });
 
    // 创建一个Vue实例来实现getter的计算属性
    this._vm = new Vue({
      data: {
        $$state: this.state
      },
      computed
    });
  }
}
 
function forEachValue(obj, fn) {
  Object.keys(obj).forEach(key => fn(obj[key], key));
}

在上述代码中,首先将传入的getters对象复制到一个新的computed对象中,然后使用Object.defineProperty将每个getter属性定义到store.getters对象上。最后,创建一个Vue实例,将computed对象传入其中,利用Vue的计算属性机制来实现getter的缓存和响应式更新。

封装getter函数#

在上面的代码中,将getter函数封装到computed对象中时,传入了this.statethis.getters作为参数。这样,getter函数就可以访问store的状态和其他getter函数。

computed[key] = () => fn(this.state, this.getters);

获取getter的值#

当组件访问store.getters中的某个属性时,会触发Object.defineProperty中定义的get方法。

Object.defineProperty(this.getters, key, {
  get: () => this._vm[key],
  enumerable: true
});

这里的this._vm[key]实际上是访问了Vue实例的计算属性,由于Vue的计算属性具有缓存功能,当依赖的状态没有发生变化时,不会重新计算,从而提高了性能。

常见实践和最佳实践#

常见实践#

  • 简单数据过滤:在getters中使用数组的过滤方法来过滤状态数据。
const getters = {
  filteredProducts: state => state.products.filter(product => product.price < 100)
};
  • 数据计算:对状态数据进行计算,如计算购物车中商品的总价格。
const getters = {
  totalPrice: state => state.cart.reduce((total, item) => total + item.price * item.quantity, 0)
};

最佳实践#

  • 避免复杂逻辑:getters应该只包含简单的计算逻辑,避免在getters中进行复杂的异步操作或副作用操作。如果需要进行复杂的逻辑处理,可以考虑使用actions。
  • 使用命名空间:在大型项目中,为了避免getters的命名冲突,可以使用模块的命名空间。

示例用法#

以下是一个完整的示例,展示如何在Vue项目中使用Vuex的getters。

1. 定义store#

import Vue from 'vue';
import Vuex from 'vuex';
 
Vue.use(Vuex);
 
const store = new Vuex.Store({
  state: {
    messages: ['Hello', 'World']
  },
  getters: {
    // 定义一个getter,返回消息的数量
    messageCount: state => state.messages.length
  }
});
 
export default store;

2. 在组件中使用getters#

<template>
  <div>
    <p>消息数量: {{ messageCount }}</p>
  </div>
</template>
 
<script>
import { mapGetters } from 'vuex';
 
export default {
  computed: {
    ...mapGetters(['messageCount'])
  }
};
</script>

在上述示例中,定义了一个gettermessageCount,用于计算state.messages数组的长度。在组件中使用mapGetters辅助函数来获取getter的值。

总结#

通过对Vuex源码中getters实现逻辑的详细解析,我们了解到getters是基于Vue的计算属性机制实现的,具有缓存和响应式更新的特性。在实际项目中,合理使用getters可以提高代码的可维护性和性能。

参考资料#