个人技术分享

简介

Vuex 是一个专为 Vue.js 应用设计的状态管理模式。它允许你将应用中共享的状态提取出来进行集中管理,并以相应的规则保证状态以一种可预测的方式发生变化。

  • State:存储应用状态数据。
  • Getters:从 state 派生出计算属性,类似于组件的 computed 属性。
  • Mutations:同步修改 state 的方法。
  • Actions:包含异步操作的函数,可调用 mutations 以最终修改 state。
  • Modules:将状态和业务逻辑分割到独立模块中。

vite 引入 vuex

npm

npm install vuex@next

yarn

yarn add vuex@next

创建一个 store 目录并新建 index.ts 文件

// src/store/index.ts
import { createStore } from 'vuex';

// 创建 Vuex 仓库
const store = createStore({
  state: {
    count: 0
  },
  mutations: {
    increment(state) {
      state.count += 1;
    },
    decrement(state) {
      state.count -= 1;
    }
  },
  actions: {
    incrementAsync({ commit }) {
      setTimeout(() => {
        commit('increment');
      }, 1000);
    }
  },
  getters: {
    doubleCount(state) {
      return state.count * 2;
    }
  }
});

export default store;

在 main.ts 中引入并使用 store

// src/main.ts
import { createApp } from 'vue';
import App from './App.vue';
import store from './store';

const app = createApp(App);
app.use(store);
app.mount('#app');

vuex的使用

<template>
  <div>
    <h1>Vuex 使用示例</h1>
    <p>Count: {{ count }}</p>
    <p>Double Count (getter): {{ doubleCount }}</p>
    <button @click="increment">Increment</button>
    <button @click="decrement">Decrement</button>
    <button @click="incrementAsync">Increment Async</button>
  </div>
</template>

<script setup lang="ts">
import { computed } from 'vue';
import { useStore } from 'vuex';

// 使用 Vuex 仓库
const store = useStore();

// 计算属性
const count = computed(() => store.state.count);
const doubleCount = computed(() => store.getters.doubleCount);

// 方法
const increment = () => store.commit('increment');
const decrement = () => store.commit('decrement');
const incrementAsync = () => store.dispatch('incrementAsync');
</script>

Vuex核心概念

state

state 是 Vuex 中的数据源,存储应用的共享状态。通过组件访问 state 中的值,可以实现数据共享。

// store/index.ts
const store = createStore({
  state: {
    count: 0,
    user: {
      name: 'Alice',
      age: 30
    }
  }
});

多个组件需要访问相同的数据(例如,用户信息,购物车状态)。

getters

getters 类似于计算属性,它从 state 中派生出一些属性,并可在组件中直接访问。

// store/index.ts
const store = createStore({
  state: {
    items: [
      { name: 'item1', price: 10 },
      { name: 'item2', price: 20 },
      { name: 'item3', price: 15 }
    ]
  },
  getters: {
    totalPrice(state) {
      return state.items.reduce((total, item) => total + item.price, 0);
    }
  }
});
<!-- src/App.vue -->
<template>
  <div>
    <h1>Shopping Cart</h1>
    <ul>
      <li v-for="item in items" :key="item.name">{{ item.name }} - ${{ item.price }}</li>
    </ul>
    <h2>Total Price: ${{ totalPrice }}</h2>
  </div>
</template>

<script setup lang="ts">
import { computed } from 'vue';
import { useStore } from 'vuex';

// 获取 Vuex Store 实例
const store = useStore();

// 计算属性来获取 Vuex 状态和 Getters
const items = computed(() => store.state.items);
const totalPrice = computed(() => store.getters.totalPrice);
</script>

从 state 中派生出需要频繁使用的计算属性(例如,计算总价格、过滤列表)。

mutations

mutations 是唯一允许同步更改 state 数据的地方。它接收两个参数:state 和 payload(可选)。

// store/index.ts
interface User{
    name:string;
    age:number;
}
interface State{
    user:User;
}
const store = createStore<State>({
  state: {
    user: {
      name: 'Alice',
      age: 30
    }
  },
  mutations: {
    updateUser(state, payload:Partial<User>) {
      state.user = { ...state.user, ...payload };
    }
  }
});

提示:...用于对象合并 避免了元对象属性的丢失。 Partial<User> 而不是直接使用 User,是为了支持部分更新的功能。

<!-- src/App.vue -->
<template>
  <div>
    <h1>User Information</h1>
    <p>Name: {{ user.name }}</p>
    <p>Age: {{ user.age }}</p>

    <h2>Update User Information</h2>
    <input v-model="newName" placeholder="New Name" />
    <input v-model.number="newAge" placeholder="New Age" type="number" />
    <button @click="updateUserInfo">Update User</button>
  </div>
</template>

<script setup lang="ts">
import { computed, ref } from 'vue';
import { useStore } from 'vuex';

// 获取 Vuex Store 实例
const store = useStore();

// 计算属性访问 Vuex 状态
const user = computed(() => store.state.user);

// 输入框绑定的数据
const newName = ref(user.value.name);
const newAge = ref(user.value.age);

// 更新用户信息
const updateUserInfo = () => {
  store.commit('updateUser', {
    name: newName.value,
    age: newAge.value
  });
};
</script>

需要同步改变共享状态时(例如,更新用户信息、添加商品到购物车)。

actions

actions 类似于 mutations,但它允许异步操作。可以在 actions 中发起网络请求或执行其他异步任务,然后再调用 mutations。

// store/index.ts
// store/index.ts
const store = createStore({
  state: {
    user: null
  },
  mutations: {
    setUser(state, user) {
      state.user = user;
    }
  },
  actions: {
    async fetchUser({ commit }) {
      const response = await fetch('https://api.example.com/user');
      const user = await response.json();
      commit('setUser', user);
    }
  }
});

需要异步改变共享状态时(例如,获取服务器数据并更新到状态中)。。

modules

modules 允许我们将 Vuex 的 state、mutations、actions 和 getters 分割成更小的模块,从而实现状态的模块化管理。

// store/modules/counter.ts
import { Module } from 'vuex';

const counterModule: Module<any, any> = {
  namespaced: true,
  state: {
    count: 0
  },
  mutations: {
    increment(state) {
      state.count += 1;
    },
    decrement(state) {
      state.count -= 1;
    }
  },
  getters: {
    doubleCount(state) {
      return state.count * 2;
    }
  },
  actions: {
    incrementAsync({ commit }) {
      setTimeout(() => {
        commit('increment');  
      }, 1000);
    }
  }
};

export default counterModule;

在全局store中使用模块

// store/index.ts
import { createStore } from 'vuex';
import counterModule from './modules/counter';

const store = createStore({
  modules: {
    counter: counterModule
  }
});

export default store;

组件使用模块状态

<template>
  <div>
    <h2>Counter Module Example</h2>
    <p>Count: {{ count }}</p>
    <p>Double Count (getter): {{ doubleCount }}</p>
    <button @click="increment">Increment</button>
    <button @click="decrement">Decrement</button>
    <button @click="incrementAsync">Increment Async</button>
  </div>
</template>

<script setup lang="ts">
import { computed } from 'vue';
import { useStore } from 'vuex';

const store = useStore();

const count = computed(() => store.state.counter.count);
const doubleCount = computed(() => store.getters['counter/doubleCount']);
const increment = () => store.commit('counter/increment');
const decrement = () => store.commit('counter/decrement');
const incrementAsync = () => store.dispatch('counter/incrementAsync');
</script>

提示:模块名称作为命名空间前缀在组件中调用 Vuex 模块的 mutations、actions 和 getters 时,需要在名称前加上模块名称。例如,counter/increment、counter/doubleCount。模块注册在全局 store 中你需要在全局的 Vuex store 实例中将模块注册为一个 module,这样才能在组件中访问它们的状态。

拓展

actions 和 mutations区别

mutations:

  • 只能进行同步操作。
  • 直接修改 state。
  • 使用 commit 来调用。
  • 每个 mutation 都会生成一个新的状态快照,方便调试工具追踪状态变化。

actions:

  • 支持异步操作。
  • 不直接修改 state,通过提交 mutations 来修改状态。
  • 使用 dispatch 来调用。
  • 可以包含复杂的逻辑,比如 API 请求、异步任务等。