Vuex的使用指南

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它用于集中管理应用程序的所有组件之间共享的数据,使得状态管理更加方便和可预测。

Vuex 主要包含以下几个核心概念:

  1. State(状态):存储应用程序的状态,即数据。在 Vuex 的 state 中定义数据,可以通过 $store.state 或使用辅助函数 mapState 在组件中访问。

  2. Getters(计算属性):用于派生状态的函数,在 Vuex 的 getters 中定义。它们可以缓存计算结果,提供组件对状态的访问方式。可以通过 $store.getters 或使用辅助函数 mapGetters 在组件中访问。

  3. Mutations(突变):用于修改状态的方法,必须是同步函数。在 Vuex 的 mutations 中定义,通过提交 (commit) Mutation 来修改状态。例如:store.commit('mutationName', payload)

  4. Actions(动作):用于处理异步操作和提交多个 mutation 的方法。在 Vuex 的 actions 中定义,通过分发 (dispatch) Action 来触发异步操作和提交 Mutation。例如:store.dispatch('actionName', payload)

  5. Modules(模块):用于将大型的 Vuex 应用程序划分为更小、更具可维护性的模块。每个模块都有自己的 state、getters、mutations 和 actions。

使用 Vuex 可以帮助我们集中管理应用程序的状态,确保数据的一致性和可追踪性。它适用于中大型的 Vue.js 应用程序,特别是多个组件之间需要共享状态或进行复杂数据交互的场景。

State状态

State 是唯一一个可以直接访问的属性,其他的属性如 Getters、Mutations 和 Actions 都需要通过 State 来获取或修改数据。State 可以被多个组件共享使用,这样就能够保持组件之间的数据同步。

你可以通过在 Vuex store 的 state 属性中定义数据,例如:

const store = new Vuex.Store({
state: {
  count: 0, // 整数
  price: 9.99, // 浮点数
  username: 'John', // 字符串
  loggedIn: true, // 布尔值
  fruits: ['apple', 'banana', 'orange'], // 数组
  user: { // 对象
    name: 'John',
    age: 30,
    email: 'john@example.com'
  }
}
});

Getters计算属性

在 Vuex 中,Getters(计算属性)用于派生状态的函数。它们可以对 state 进行基于 state 的计算或数据转换,并可以被组件访问和使用。

计算属性具有缓存机制,只有在依赖的数据发生变化时,计算属性才会重新计算。这样可以避免不必要的重复计算,提高性能。

下面是一个示例:

const store = new Vuex.Store({
  state: {
    items: [1, 2, 3, 4, 5]
  },
  getters: {
    evenItems: (state) => {
      return state.items.filter(item => item % 2 === 0);
    },
    itemsCount: (state, getters) => {
      return getters.evenItems.length;
    }
  }
});

在上述示例中,定义了两个计算属性:

  • evenItems:通过过滤 state.items 数组,只保留偶数项。

  • itemsCount:返回 evenItems 数组的长度,即偶数项的数目。

在组件中可以通过 $store.getters 来访问这些计算属性的值:

console.log(this.$store.getters.evenItems); // 输出 [2, 4]
console.log(this.$store.getters.itemsCount); // 输出 2

在模板中使用计算属性:

<template>
  <div>
    <p>Even Items: {{ $store.getters.evenItems }}</p>
    <p>Items Count: {{ $store.getters.itemsCount }}</p>
  </div>
</template>

计算属性的值会自动更新,当它所依赖的 state 发生变化时。这使得我们能够轻松地基于 state 的变化来更新组件的显示。

计算属性在处理一些复杂的状态派生和数据转换时非常有用,可以将复杂的计算逻辑封装在计算属性中,让代码更易于理解和维护。

Mutations突变

在 Vuex 中,Mutations(突变)用于修改状态(state)的函数。它们是唯一允许修改 state 的方式,并且必须是同步的操作。

Mutations 接收两个参数:第一个参数是当前的状态(state),第二个参数是负载(payload),即传递给 Mutation 的数据。

下面是一个示例:

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment(state) {
      state.count++;
    },
    decrement(state, payload) {
      state.count -= payload;
    }
  }
});

在上述示例中,定义了两个 Mutations:

  • increment:将 state.count 值增加 1。

  • decrement:从 state.count 中减去传入的 payload 值。

在组件中可以通过 $store.commit 方法来触发 Mutations:

this.$store.commit('increment');
this.$store.commit('decrement', 5);

每次调用 commit 方法时,对应的 Mutation 函数会被执行,从而修改 state 中的数据。

或者使用 mapMutations 辅助函数:Vuex 提供了 mapMutations 辅助函数,可以将 Mutations 映射到组件的方法中,方便在组件中直接调用 Mutations。

import { mapMutations } from 'vuex';

export default {
  // ...
  methods: {
    ...mapMutations(['increment', 'decrement']),
    // 或者给 Mutations 起别名
    ...mapMutations({
      add: 'increment'
    })
  }
}

需要注意的是,Mutations 必须是同步的操作,因此不能在 Mutations 中进行异步操作。如果需要进行异步操作,可以使用 Actions。

Mutations 的作用类似于事件,通过触发 Mutations 来修改 state,以保证状态的变更是可追踪和可预测的。

Actions动作

在 Vuex 中,Actions(动作)用于处理异步操作、封装业务逻辑或者触发多个 Mutations 的函数。Actions 可以包含任意的异步操作,例如发送网络请求、定时器等。Actions 提供了一种将异步操作和状态变更分离的方式。

Actions 接收一个 Context 对象作为参数,其中包含了与 store 实例具有相同方法和属性的对象,例如 statecommitdispatchgetters 等。通过调用 commit 方法来触发 Mutations,从而修改状态。

下面是一个示例:

const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment(state) {
      state.count++;
    },
    decrement(state, payload) {
      state.count -= payload;
    }
  },
  actions: {
    incrementAsync(context) {
      setTimeout(() => {
        context.commit('increment');
      }, 1000);
    },
    decrementAsync(context, payload) {
      setTimeout(() => {
        context.commit('decrement', payload);
      }, 1000);
    }
  }
});

在上述示例中,定义了两个 Actions:

  • incrementAsync:在延迟 1 秒后,调用 commit 方法触发 increment Mutations。

  • decrementAsync:在延迟 1 秒后,调用 commit 方法触发 decrement Mutations,并传递 payload 参数。

在组件中可以通过 $store.dispatch 方法来触发 Actions:

this.$store.dispatch('incrementAsync');
this.$store.dispatch('decrementAsync', 5);

每次调用 dispatch 方法时,对应的 Action 函数会被执行。Action 函数中可以进行异步操作,并通过调用 commit 方法来触发 Mutations。

需要注意的是,Actions 是可以处理异步操作的,因此可以在 Action 函数中包含任意异步代码。在异步操作完成后,再通过调用 commit 方法来触发 Mutations 进行状态的修改。

类似于 Mutations,也可以使用对象风格的提交、在模块中使用 dispatch、使用 mapActions 辅助函数等方式来触发 Actions。

Modules模块

在 Vuex 中,Modules(模块)用于将 Vuex 的状态和操作分割为可管理的模块。模块化可以使得大型应用程序的状态管理更加清晰、可维护性更高。

通过模块化,可以将一个大型的 Vuex 应用程序拆分成多个小的模块,每个模块都有自己的 state、mutations、actions 和 getters。

下面是一个示例:

const moduleA = {
  state: {
    count: 0
  },
  mutations: {
    increment(state) {
      state.count++;
    }
  },
  actions: {
    incrementAsync(context) {
      setTimeout(() => {
        context.commit('increment');
      }, 1000);
    }
  },
  getters: {
    doubleCount(state) {
      return state.count * 2;
    }
  }
};

const moduleB = {
  // ...
};

const store = new Vuex.Store({
  modules: {
    moduleA,
    moduleB
  }
});

在上述示例中,定义了两个模块 moduleAmoduleB,它们分别有自己的 state、mutations、actions 和 getters。然后,在创建 Vuex Store 实例时,通过 modules 选项指定这些模块。

在组件中可以通过 $store.state.moduleA 来访问模块的状态,例如:this.$store.state.moduleA.count

类似地,可以使用 $store.commit$store.dispatch$store.getters 来触发模块中的 Mutations、Actions 和 Getters。

需要注意的是,在模块中触发 Mutations 和 Actions 时,需要指定模块的路径,例如:this.$store.commit('moduleA/increment')

模块化不仅可以帮助组织代码,还能够解决命名冲突、提高可复用性,并且可以将大型应用程序分割为更小的部分进行开发和维护。

案例

定义要共享的属性:

src\store\index.js

import { createStore } from 'vuex'

export default createStore({
  state: {
	  projectId: ''
  },
  
  // 定义全局的计算属性,类似于vue组件中的计算属性
  getters: {
  },
  
  mutations: {
	// 必须设置两个参数,第一个参数为state对象,第二个参数为调用该方法时传递的参数
	updateProjectId(state, newID){
		state.projectId = newID;
	},
  },
  
  // 定义全局的异步方法,如果有后端接口请求,放在此处定义
  actions: {
  },
  
  // 模块(当项目结构大且复杂的时候,会对项目中的全局数据分模块管理)
  modules: {
  }
})

修改要共享的属性

src\views\AllProject.vue

<script>
import { mapState, mapMutations } from 'vuex';
export default {
  data() {
    return {
      // 项目列表
      projects: [],
      // 定义项目id
      id:'',
      // 编辑默认弹框
      showEditDialog: false,
      // 添加项目默认弹框
      showAddDialog:false,
      // 删除默认弹框
      showDelDialog: false,
      // 用于接收用户编辑的项目信息
			editForm: {
				name: '',
				leader: '',
			},
      // 用于接收用户输入的项目信息
      addForm:{
        name:'',
        leader:'',
      }
    }
  },

  methods: {
    // 更新projectId:方法1
   ...mapMutations(['updateProjectId']),
   goToProjectDetail(id){
   console.log('Navigating to Project with ID:', id);
   this.updateProjectId(id);
   this.$router.push({ name: 'Project' });
   }
  }
}
</script>

这里使用了 mapMutations 辅助函数来映射 Mutations 到组件的方法中。

mapMutations 是 Vuex 提供的一个辅助函数,用于将指定的 Mutations 映射为组件的方法,使得可以直接在组件中调用这些 Mutations。

在你的示例代码中,通过 ...mapMutations(['updateProjectId'])updateProjectId Mutation 映射为组件的方法。

然后,在 goToProjectDetail 方法中,首先打印出项目的 ID,然后调用映射的 updateProjectId 方法,将 ID 作为参数传递给该 Mutations。接着使用 $router.push 方法进行导航,跳转到名为 "Project" 的路由。

这样,当 goToProjectDetail 方法被调用时,先触发 Mutations 更新项目 ID 的状态,然后进行路由导航。

需要注意的是,映射的 Mutations 方法会自动和组件的 this.$store.commit 方法绑定,因此可以直接调用,无需手动调用 this.$store.commit

或者直接使用this.$store.commit 方法

    // 更新projectId:方法2
    goToProjectDetail(id) {
    console.log('Navigating to Project with ID:', id);
    this.$store.commit('updateProjectId', id);
    this.$router.push({ name: 'Project' });
  }

goToProjectDetail 方法中,直接使用 this.$store.commit 方法来触发名为 'updateProjectId' 的 Mutations,并将 id 作为参数传递给该 Mutations。接着使用 $router.push 方法进行路由导航。

这样,当 goToProjectDetail 方法被调用时,先触发 Mutations 更新项目 ID 的状态,然后进行路由导航。

需要注意的是,使用 this.$store.commit 触发 Mutations 时,需要指定该 Mutations 的名称和参数,即 'updateProjectId'id

调用要修改的属性

src\views\Project.vue

 computed: {
		// 方法一:可以使用mapState辅助函数,将state中的全局数据映射到组件中的computed计算属性
		...mapState(['projectId'])
	},
 async created() {
        const projectId = this.projectId
        console.log('ID:', projectId);
        await this.getProjectEnv(projectId);
        let options = [];
        for (let key in this.env) {
          if (typeof this.env[key] === 'object' && this.env[key].hasOwnProperty('name')) {
            options.push({
              value: this.env[key].id,
              label: this.env[key].name,
              id:this.env[key].id
            });
          }
        }
        this.options = options;
      },

使用了 mapState 辅助函数将 Vuex 中的 projectId 映射到组件的计算属性中。

在组件的 computed 中,通过 ...mapState(['projectId'])projectId 映射为组件的计算属性。

或者直接使用this.$store.state.projectId

 async created() {
        const projectId = this.$store.state.projectId
        console.log('ID:', projectId);
        await this.getProjectEnv(projectId);
        let options = [];
        for (let key in this.env) {
          if (typeof this.env[key] === 'object' && this.env[key].hasOwnProperty('name')) {
            options.push({
              value: this.env[key].id,
              label: this.env[key].name,
              id:this.env[key].id
            });
          }
        }
        this.options = options;
      },

直接使用 this.$store.state.projectId 获取 Vuex 中的 projectId

created 生命周期函数中,首先获取 projectId 值并打印出来。

若是在模板中调用属性值

在 Vue 的模板中,可以通过双大括号 {{ }} 或者指令 v-bind 来调用属性值。

如果你想在模板中调用 projectId 的属性值,可以按照以下方式进行修改:

<template>
  <div>
    <p>Project ID: {{ $store.state.projectId }}</p>
    <!-- 或者使用 v-bind 指令 -->
    <p>Project ID: <span v-bind:text="$store.state.projectId"></span></p>
  </div>
</template>

在上述示例中,使用双大括号 {{ }} 插入 $store.state.projectId 属性的值,并显示在 <p> 元素中。

或者使用使用 mapState 函数

<template>
  <div>
    <p>Project ID: {{ $store.state.projectId }}</p>
    <el-tag>{{ projectId}}</el-tag>
  </div>
</template>

<script>
import { mapState} from 'vuex';
export default {
	computed: {
		// 不推荐
		// projectId () {
		// 	return this.store.state.projectId;
		// },
		
		// 方法二:可以使用mapState辅助函数,将state中的全局数据映射到组件中的computed计算属性
		...mapState(['projectId'])
	},
}
</script>