当前位置: 首页 > article >正文

Vuex状态管理

1、Vuex 是什么?

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

简单理解

Vuex可以帮我们管理全局的属性,并且是是响应式的,状态的变化是可以跟踪的

什么是“状态管理模式”?

<template>
  <h3>count1</h3>
  <p>{
  
  {count}}</p>
  <button @click="add">加1</button>
</template>

<script setup>
  import {ref} from "vue";
  const count = ref(0);  //其实就是状态
  function add() {
    count.value++;
  }
</script>

这个状态自管理应用包含以下几个部分

  • 状态,驱动应用的数据源;就是count

  • 视图,以声明方式将状态映射到视图;就是将count显示到template
  • 操作,响应在视图上的用户输入导致的状态变化;就是点击按钮时count发生变化

但是,当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏,就像count在多个组件中使用时。

  • 多个视图依赖于同一状态。
  • 来自不同视图的行为需要变更同一状态

问题一,传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。

问题二,我们经常会采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码

因此,我们为什么不把组件的共享状态抽取出来,以一个全局单例模式管理呢?在这种模式下,我们的组件树构成了一个巨大的“视图”,不管在树的哪个位置,任何组件都能获取状态或者触发行为!

什么情况下我应该使用 Vuex?

Vuex 可以帮助我们管理共享状态,并附带了更多的概念和框架。这需要对短期和长期效益进行权衡

如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您最好不要使用 Vuex。一个简单的 store 模式就足够您所需了。但是,如果您需要构建一个中大型单页应用,您很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择。引用 Redux 的作者 Dan Abramov 的话说就是:您自会知道什么时候需要它

2、项目中引入Vuex

安装vuex

npm install vuex@next --save

main.js文件配置vuex

import { createApp } from 'vue'
import App from './App.vue'
//1.引入vuex
import {createStore} from "vuex";

//2.创建vuex对象
const store = createStore({
    //3.创建状态
        state:{
            count:10,
        }
})

//4.挂载vuex
const app = createApp(App);
app.use(store)
app.mount('#app')

 在count1和count2页面上显示

<template>
  <h3>count1</h3>
  <p>{
  
  { $store.state.count}}</p>
</template>

 一般是将vuex的配置放在一个单独的文件并到出,在main.js文件引入。

//1.引入vuex
import {createStore} from "vuex";
//2.创建vuex对象
const store = createStore({
    //3.创建状态
    state:{
        count:10
    }
})
export default store
import { createApp } from 'vue'
import App from './App.vue'
import store from './store'

//4.挂载vuex
const app = createApp(App)
app.use(store)
app.mount('#app')

3、核心概念-State

Vuex 使用单一状态树,用一个对象就包含了全部的应用层级状态。至此它便作为一个“唯一数据源 ”而存在。这也意味着,每个应用将仅仅包含一个 store 实例。换言之,vuex全部的数据都是存放在state中的。

import {createStore} from "vuex";
const store = createStore({
    state:{
        count:10,
        message:'vuex单一的数据'
    }
})
export default store
<template>
  <!-- 显示标题 -->
  <h3>count1,获取vuex数据的两种方式</h3>
  <!-- 方式一:直接使用 $store 获取 Vuex 中的 state 数据 -->
  <p>方式一:{
  
  { $store.state.count }}</p>
  <!-- 方式二:通过计算属性 count 获取 Vuex 中的 state 数据 -->
  <p>方式二:{
  
  { count }}</p>
</template>

<script setup>
// 引入 Vue 的 computed 函数,用于创建计算属性
import { computed } from "vue";
// 引入 Vuex 的 useStore 函数,用于获取 Vuex store 实例
import { useStore } from "vuex";

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

// 定义一个计算属性 count,用于从 Vuex store 中获取 state 的 count 值
// computed 会监听 store.state.count 的变化,并在变化时自动更新 count 的值
const count = computed(() => store.state.count);
</script>

mapState 辅助函数

只能在选项式API存在

<template>
  <h3>count2</h3>
  <!-- 显示从 Vuex 获取的 `count` 状态 -->
  <p>{
  
  { count }}</p>
  <!-- 显示从 Vuex 获取的 `message` 状态 -->
  <p>{
  
  { message }}</p>
</template>

<script>
// 从 Vuex 中引入 mapState 辅助函数
import { mapState } from "vuex";

export default {
  // 使用 Vue 的 computed 属性
  computed: {
    // 使用扩展运算符 (...), 将 Vuex 中的 state 映射到本地计算属性
    ...mapState(["count", "message"]),
    /*
    以上代码等价于以下手动定义的计算属性:
    count() {
      return this.$store.state.count;
    },
    message() {
      return this.$store.state.message;
    }
    */
  }
};

4、核心概念-Getter

Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)

添加Getters

import {createStore} from "vuex";
const store = createStore({
    state:{
        count:10,
        message:'vuex单一的数据'
    },
    getters:{
        getCount(state){
            return "当前的count为:"+state.count
        }
    }
})
export default store

选项式API获取Getters

<template>
<h3>count2</h3>
  <p>直接在标签中获取:{
  
  {$store.getters.getCount}}</p>
<p>通过计算属性获取:{
  
  {getCount}}</p>
</template>

<script>

export default {
  computed:{
    getCount(){
      return this.$store.getters.getCount;
    }
  }
}
</script>

组合式API获取Getters

<template>
  <h3>count1</h3>
  <p>直接在标签获取:{
  
  { $store.getters.getCount}}</p>
  <p>计算属性获取:{
  
  {currentCount}}</p>
</template>

<script setup>
import {computed} from "vue";
import {useStore} from "vuex";
const store = useStore();
const currentCount = computed(() => {
  return store.getters.getCount;
})
</script>

mapGetters 辅助函数

mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性。

<template>
<h3>count2</h3>
  <p>直接在标签中获取:{
  
  {$store.getters.getCount}}</p>
  <p>通过mapGetters获取:{
  
  {getCount}}</p>
</template>

<script>
import {mapGetters} from "vuex";

export default {
  computed:{
    ...mapGetters(['getCount'])
  }
}
</script>

 5、核心概念-Mutation

更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的事件类型 (type)和一个回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方

在Vue中添加mutation

import { createStore } from 'vuex'
const store = createStore({
  state:{
    count: 10
   },
  getters: {
    getCount(state) {
      return "当前Count值为: "+state.count;
     }
   },
  mutations:{
    increment(state){
      state.count++
     },
    decrement(state){
      state.count--
     }
   }
})
export default store

选项式API使用mutation

<template>
  <h3>count2</h3>
  <!-- 显示从 Vuex 的 getters 中获取的值 -->
  <p>{
  
  { getCount }}</p>
  <!-- 点击按钮调用 addHandler 方法,触发 Vuex 的 increment mutation -->
  <button @click="addHandler">增加</button>
  <!-- 点击按钮调用 minHandler 方法,触发 Vuex 的 decrement mutation -->
  <button @click="minHandler">减少</button>
</template>

<script>
// 从 Vuex 中引入 mapGetters 辅助函数
import { mapGetters } from "vuex";

export default {
  // 定义计算属性
  computed: {
    // 使用 mapGetters 将 Vuex 的 getters 映射到本地计算属性
    // 这里将 Vuex 的 getCount getter 映射到本地的 getCount 属性
    ...mapGetters(['getCount'])
  },
  // 定义组件的方法
  methods: {
    // 定义增加按钮的点击事件处理函数
    addHandler() {
      // 调用 Vuex 的 commit 方法,触发 increment mutation
      this.$store.commit('increment');
    },
    // 定义减少按钮的点击事件处理函数
    minHandler() {
      // 调用 Vuex 的 commit 方法,触发 decrement mutation
      this.$store.commit('decrement');
    }
  }
};

组合式API使用mutation

<template>
  <h3>count1</h3>
  <!-- 显示当前计数器的值,通过计算属性 currentCount 获取 -->
  <p>{
  
  { currentCount }}</p>
  <!-- 点击按钮调用 addHandler 方法,通过 Vuex 的 increment mutation 增加计数 -->
  <button @click="addHandler">增加</button>
  <!-- 点击按钮调用 minHandler 方法,通过 Vuex 的 decrement mutation 减少计数 -->
  <button @click="minHandler">减少</button>
</template>

<script setup>
import { computed } from "vue"; // 引入 Vue 的 computed 函数,用于定义计算属性
import { useStore } from "vuex"; // 引入 Vuex 的 useStore 函数,用于获取 Vuex store 实例

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

// 定义一个计算属性 currentCount,用于从 Vuex 的 getters 中获取值
const currentCount = computed(() => {
  // 通过 store.getters 获取 Vuex 的计数器值
  return store.getters.getCount;
});

// 定义增加按钮的点击事件处理函数
const addHandler = () => {
  // 调用 Vuex 的 commit 方法,触发 increment mutation
  store.commit("increment");
};

// 定义减少按钮的点击事件处理函数
const minHandler = () => {
  // 调用 Vuex 的 commit 方法,触发 decrement mutation
  store.commit("decrement");
};
</script>

Mutation-携带参数

你可以向 store.commit 传入额外的参数,即 mutation 的载荷(payload)。

vuex配置

import { createStore } from 'vuex' // 引入Vuex的createStore方法,用于创建Vuex存储实例

// 创建Vuex存储实例
const store = createStore({
    // 定义状态(state):Vuex中存储应用的数据,所有组件都可以访问这些数据
    state: {
        count: 10 // 定义一个名为count的状态,初始值为10
    },
    // 定义Getter:用于从state中派生出一些状态,相当于计算属性,可以对state进行加工
    getters: {
        // 定义一个名为getCount的Getter,接收state作为参数
        getCount(state) {
            return "当前Count值为: " + state.count; // 返回一个字符串,包含state中的count值
        }
    },
    // 定义Mutations:用于更改state中的数据,是唯一可以同步修改state的地方
    mutations: {
        // 定义一个名为increment的Mutation,接收state和num作为参数
        increment(state, num) {
            state.count += Number(num); // 将state中的count值加上num的数值
        },
        // 定义一个名为decrement的Mutation,接收state和num作为参数
        decrement(state, num) {
            state.count -= Number(num); // 将state中的count值减去num的数值
        }
    },
 }
})

// 导出Vuex存储实例,以便在Vue应用中使用
export default store

选项式API传递参数

<template>
  <!-- 显示一个标题 -->
  <h3>count2</h3>
  <!-- 显示从 Vuex store 中获取的 getCount 值 -->
  <p>{
  
  { getCount }}</p>
  <!-- 输入框,用于输入要增加或减少的数值,v-model 实现双向绑定 -->
  <input type="text" v-model="num" />
  <!-- 增加按钮,点击时调用 addHandler 方法 -->
  <button @click="addHandler">增加</button>
  <!-- 减少按钮,点击时调用 minHandler 方法 -->
  <button @click="minHandler">减少</button>
</template>

<script>
// 引入 Vuex 的 mapGetters 辅助函数,用于将 Vuex 的 getters 映射到组件的 computed 属性中
import { mapGetters } from "vuex";

export default {
  // 定义组件的 data 属性
  data() {
    return {
      // 输入框的绑定变量,初始为空字符串
      num: ""
    };
  },
  // 计算属性
  computed: {
    // 使用 mapGetters 将 Vuex 中的 getCount getter 映射到组件的计算属性中
    ...mapGetters(["getCount"])
  },
  // 方法
  methods: {
    // 增加按钮点击事件处理函数
    addHandler() {
      // 调用 Vuex 的 commit 方法,提交名为 'increment' 的 mutation,并传递当前输入框的值
      this.$store.commit("increment", this.num);
    },
    // 减少按钮点击事件处理函数
    minHandler() {
      // 调用 Vuex 的 commit 方法,提交名为 'decrement' 的 mutation,并传递当前输入框的值
      this.$store.commit("decrement", this.num);
    }
  }
};
</script>

 组合式API传递参数

<template>
  <!-- 显示标题 -->
  <h3>count1</h3>
  <!-- 显示当前的 count 值,该值来自 Vuex store 的计算属性 -->
  <p>{
  
  { currentCount }}</p>
  <!-- 输入框,用于输入要增加或减少的数值,通过 v-model 实现双向绑定 -->
  <input type="text" v-model="num" />
  <!-- 增加按钮,点击时调用 addHandler 方法 -->
  <button @click="addHandler">增加</button>
  <!-- 减少按钮,点击时调用 minHandler 方法 -->
  <button @click="minHandler">减少</button>
</template>

<script setup>
// 引入 Vue 的核心响应式函数
import { computed } from "vue";
// 引入 Vuex 的 useStore 函数,用于访问 Vuex store
import { useStore } from "vuex";
// 引入 Vue 的 ref 函数,用于创建响应式变量
import { ref } from "vue";

// 创建一个响应式引用变量 num,初始化为 0
const num = ref(0);

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

// 定义一个计算属性 currentCount,用于获取 Vuex store 中的 getCount 值
const currentCount = computed(() => {
  // 从 Vuex store 的 getters 中获取 getCount 的值
  return store.getters.getCount;
});

// 定义增加按钮的点击处理函数
const addHandler = () => {
  // 通过 store.commit 提交名为 'increment' 的 mutation,并传递 num 的当前值
  store.commit("increment", num.value);
};

// 定义减少按钮的点击处理函数
const minHandler = () => {
  // 通过 store.commit 提交名为 'decrement' 的 mutation,并传递 num 的当前值
  store.commit("decrement", num.value);
};
</script>

注意组合式API获取ref创建的值的时候要使用.value获取 

 对象风格的传参方式

选项式

  methods:{
    addHandler(){
      this.$store.commit({
        type: 'increment',
        num: this.num
      });
    },
    minHandler(){
      this.$store.commit({
        type: 'decrement',
        num: this.num
      });
    }
  }

组合式

const addHandler = () => {
  store.commit({
    type: 'increment',
    num: this.num
  });
}
const minHandler = () => {
  store.commit({
    type: 'decrement',
    num: this.num
  });
}

注意:

此时在mutation 中获取的num参数要变为{num}

    mutations:{
        increment(state, {num}){
            state.count += Number(num)
        },
        decrement(state, {num}){
            state.count -= Number(num)
        }
    }

Mutation 必须是同步函数

一条重要的原则就是要记住 mutation 必须是同步函数

现在想象,我们正在 debug 一个 app 并且观察 devtool 中的 mutation 日志。每一条 mutation 被记录,devtools 都需要捕捉到前一状态和后一状态的快照。然而,在上面的例子中 mutation 中的异步函数中的回调让这不可能完成:因为当 mutation 触发的时候,回调函数还没有被调用,devtools 不知道什么时候回调函数实际上被调用——实质上任何在回调函数中进行的状态的改变都是不可追踪的

 Mutation-辅助函数

你可以在组件中使用 this.$store.commit('xxx') 提交 mutation,或者使用 mapMutations 辅助函数将组件中的 methods 映射为 store.commit 调用(需要在根节点注入 store

<template>
  <!-- 显示标题 -->
  <h3>count2</h3>
  <!-- 显示从 Vuex store 中获取的 getCount 值 -->
  <p>{
  
  { getCount }}</p>
  <!-- 输入框,用于输入要增加或减少的数值,通过 v-model 实现双向绑定 -->
  <input type="text" v-model="num" />
  <!-- 增加按钮,点击时调用 addHandler 方法 -->
  <button @click="addHandler">增加</button>
  <!-- 减少按钮,点击时调用 minHandler 方法 -->
  <button @click="minHandler">减少</button>
</template>

<script>
// 引入 Vuex 的 mapGetters 和 mapMutations 辅助函数
import { mapGetters, mapMutations } from "vuex";

export default {
  data() {
    return {
      // 输入框的绑定变量,初始为空字符串
      num: ""
    };
  },
  computed: {
    // 使用 mapGetters 将 Vuex 的 getters 映射到组件的计算属性中
    ...mapGetters(["getCount"])
  },
  methods: {
    // 使用 mapMutations 将 Vuex 的 mutations 映射到组件的方法中
    ...mapMutations(["increment", "decrement"]),
    // 增加按钮点击事件处理函数
    addHandler() {
      // 调用映射的 increment 方法,并传递一个对象,包含 num 属性
      this.increment({
        num: this.num // 将输入框的值传递给 mutation
      });
    },
    // 减少按钮点击事件处理函数
    minHandler() {
      // 调用映射的 decrement 方法,并传递一个对象,包含 num 属性
      this.decrement({
        num: this.num // 将输入框的值传递给 mutation
      });
    }
  }
};
</script>

6、核心概念-Action

Action 类似于 mutation,不同在于:

  • Action 提交的是 mutation,而不是直接变更状态。
  • Action 可以包含任意异步操作。

在Vue中增加Actions

import { createStore } from 'vuex' // 引入Vuex的createStore方法

// 创建Vuex存储实例
const store = createStore({
    // 定义状态(state):存储应用的数据
    state: {
        count: 10 // 定义一个名为count的状态,初始值为10
    },
    // 定义Getter:从state中派生出一些状态,相当于计算属性
    getters: {
        // 定义一个名为getCount的Getter,接收state作为参数
        getCount(state) {
            return "当前Count值为: " + state.count; // 返回一个字符串,包含state中的count值
        }
    },
    // 定义Mutation:用于更改state中的数据,必须是同步操作
    mutations: {
        // 定义一个名为increment的Mutation,接收state和解构后的num
        increment(state, { num }) {
            state.count += Number(num); // 将state中的count值加上num的数值
        },
        // 定义一个名为decrement的Mutation,接收state和解构后的num
        decrement(state, { num }) {
            state.count -= Number(num); // 将state中的count值减去num的数值
        }
    },
    // 定义Action:用于处理异步操作,可以提交Mutation来更改state
    actions: {
        // 定义一个名为asyncIncrement的Action,接收context和num作为参数
        asyncIncrement(context, num) {
            context.commit('increment', num); // 提交名为increment的Mutation,将num作为参数
        },
        // 定义一个名为asyncDecrement的Action,接收context和num作为参数
        asyncDecrement(context, num) {
            context.commit('decrement', num); // 提交名为decrement的Mutation,将num作为参数
        }
    }
})

// 导出Vuex存储实例,以便在Vue应用中使用
export default store

选项式API使用Actions

<template>
  <!-- 显示标题 -->
  <h3>count2</h3>
  <!-- 显示从 Vuex store 中获取的 getCount 值 -->
  <p>{
  
  { getCount }}</p>
  <!-- 输入框,用于输入要增加或减少的数值,通过 v-model 实现双向绑定 -->
  <input type="text" v-model="num" />
  <!-- 增加按钮,点击时调用 addHandler 方法 -->
  <button @click="addHandler">增加</button>
  <!-- 减少按钮,点击时调用 minHandler 方法 -->
  <button @click="minHandler">减少</button>
</template>

<script>
// 引入 Vuex 的 mapGetters 辅助函数
import { mapGetters } from "vuex";

export default {
  data() {
    return {
      // 输入框的绑定变量,初始为空字符串
      num: ""
    };
  },
  computed: {
    // 使用 mapGetters 将 Vuex 的 getters 映射到组件的计算属性中
    ...mapGetters(["getCount"])
  },
  methods: {
    // 增加按钮点击事件处理函数
    addHandler() {
      // 调用 Vuex 的 dispatch 方法,触发名为 'asyncIncrement' 的 action,并传递一个对象,包含 num 属性
      this.$store.dispatch("asyncIncrement", {
        num: this.num // 将输入框的值传递给 action
      });
    },
    // 减少按钮点击事件处理函数
    minHandler() {
      // 调用 Vuex 的 dispatch 方法,触发名为 'asyncDecrement' 的 action,并传递一个对象,包含 num 属性
      this.$store.dispatch("asyncDecrement", {
        num: this.num // 将输入框的值传递给 action
      });
    }
  }
};
</script>

组合式API使用Actions

<template>
  <h3>count1</h3>
  <p>{
  
  { currentCount }}</p>
  <input type="text" v-model="num">
  <button @click="addHandler">增加</button>
  <button @click="minHandler">减少</button>
</template>

<script setup>
import {computed} from "vue";
import {useStore} from "vuex";
import {ref} from "vue";

const num = ref("");

const store = useStore();
const currentCount = computed(() => {
  return store.getters.getCount;
})

const addHandler = () => {
    store.dispatch("asyncIncrement", num.value);
}
const minHandler = () => {
  store.dispatch("asyncDecrement",num.value);
}
</script>

Action-异步操作

安装依赖

npm install --save axios

实现异步Action

import { createStore } from 'vuex' // 引入 Vuex 的 createStore 方法
import axios from "axios" // 引入 Axios,用于发送 HTTP 请求

// 创建 Vuex 存储实例
const store = createStore({
  // 定义状态(state):存储应用的数据
  state: {
    // 定义一个名为 banner 的状态,用于存储从接口获取的数据
    banner: []
  },
  // 定义 Mutation:用于更改 state 中的数据,必须是同步操作
  mutations: {
    // 定义一个名为 setBanner 的 Mutation,接收 state 和 banner 参数
    setBanner(state, banner) {
      // 将传入的 banner 数据赋值给 state.banner
      state.banner = banner;
    }
  },
  // 定义 Action:用于处理异步操作,可以提交 Mutation 来更改 state
  actions: {
    // 定义一个名为 asyncSetBanner 的 Action,接收 context 和 url 参数
    asyncSetBanner(context, url) {
      // 使用 Axios 发送 GET 请求到指定的 URL
      axios
        .get(url)
        .then((res) => {
          // 请求成功后,调用 Mutation 的 setBanner 方法,并将返回的 banner 数据传递给它
          context.commit("setBanner", res.data.banner);
        })
        .catch((error) => {
          // 请求失败时,可以在这里处理错误
          console.error("获取 banner 数据失败:", error);
        });
    }
  }
});

// 导出 Vuex 存储实例,以便在 Vue 应用中使用
export default store;

banner.vue组件

<template>
  <!-- 点击按钮时调用 getBannerHandler 方法 -->
  <button @click="getBannerHandler">获取数据</button>
  <!-- 使用 v-for 循环渲染 banner 数据 -->
  <ul>
    <!-- 遍历 $store.state.banner 数组,输出每个元素的 title 和 content -->
    <li v-for="(item, index) in $store.state.banner" :key="index">
      <h3>{
  
  { item.title }}</h3>
      <p>{
  
  { item.content }}</p>
    </li>
  </ul>
</template>

<script setup>
// 引入 Vuex 的 useStore 函数,用于访问 Vuex store
import { useStore } from "vuex";

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

// 定义一个函数,用于触发获取 banner 数据的 action
function getBannerHandler() {
  // 调用 Vuex 的 dispatch 方法,触发名为 asyncSetBanner 的 action,并传递一个 URL 参数
  store.dispatch("asyncSetBanner", "http://iwenwiki.com/api/blueberrypai/getIndexBanner.php");
}
</script>

 Action辅助函数

也是只能在选项式API使用。

<template>
  <button @click="getBannerHandler">获取数据</button>
  <ul>
    <li v-for="(item,index) in $store.state.banner" :key="index">
      <h3>{
  
  { item.title }}</h3>
      <p>{
  
  { item.content }}</p>
    </li>
  </ul>
</template>
<script>
import { mapActions } from "vuex"
export default {
  methods:{
    ...mapActions(["asyncSetBanner"]),
    getBannerHandler(){
      this.asyncSetBanner("http://iwenwiki.com/api/blueberrypai/getIndexBanner.php")
     }
   }
}
</script>

7、核心概念-Module

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿

为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割。

在store下创建Login文件夹并在文件夹下创建index.js文件

export default {
    state:{
        token:"asdasdlhcHDIHIO"
    }
}

在store下创建Order文件夹并在文件夹下创建index.js文件

export default {

    state:{
        order:[
            {
                id:101,
                name:"佳洁士"
            },
            {
                id:102,
                name: "舒肤佳"
            }
        ]
    }

}

在store主文件引入模块

import { createStore } from 'vuex'
import Login from "@/store/Login/index.js";
import Order from "@/store/Order/index.js";
const store = createStore({
    modules:{
        Login,
        Order
    }
})
export default store

 在Login.vue页面获取token数据

<template>
  <h3>登录页面</h3>
  <p>{
  
  {token}}</p>
</template>

<script setup>
  import {computed} from "vue";
  import {useStore} from "vuex";

  const store = useStore();

  const token = computed(() => store.state.Login.token);
</script>

 在Order.vue页面获取order数据

<template>
  <h3>订单页面</h3>
  <ul>
    <li v-for="(item,index) of order" :key="index">
      <p>{
  
  {item.id}}</p>
      <p>{
  
  {item.name}}</p>
    </li>
  </ul>
</template>

<script setup>
import {computed} from "vue";
import {useStore} from "vuex";
const store = useStore();
const order = computed(()=>{
    return store.state.Order.order;
})
</script>

挂在到APP.vue页面上并显示

 Module-模块的局部状态

每个模块拥有自己的 state、mutation、action、getter

这里拿login模块举例子

export default {
    state: {
        token: "asdasdlhcHDIHIO" // 定义初始状态,存储 token 值
    },
    // rootState: 顶级的 state,可以访问其他 module 或根 Vuex store 的 state
    getters: {
        getToken(state, getters, rootState) {
            // 定义一个 getter,用于格式化 token 的输出
            // state: 当前 module 的 state
            // getters: 当前 module 的其他 getter
            // rootState: 整个 Vuex store 的根 state(如果需要访问其他 module 的 state)
            return "当前的 token 为: " + state.token; // 返回格式化后的 token 文本
        }
    },
    // 修改 state
    mutations: {
        updateToken(state, token) {
            // 定义一个 mutation,用于更新 state 中的 token 值
            // state: 当前 module 的 state
            // token: 传入的新的 token 值
            state.token = token; // 将传入的 token 值赋值给 state.token
        }
    },
    // 调用 mutations
    // rootState: 主文件中的根 state
    actions: {
        asyncUpdateToken({ commit, rootState, state }, token) {
            // 定义一个 action,用于异步更新 token 值
            // commit: 用于提交 mutation
            // rootState: 整个 Vuex store 的根 state
            // state: 当前 module 的 state
            // token: 传入的新的 token 值
            commit('updateToken', token); // 调用 updateToken mutation,更新 token
        }
    }
}
<template>
  <h3>登录页面</h3>
  <p>{
  
  {token}}</p>
  <p>{
  
  {currentToken}}</p>
  <input type="text" v-model="newtoken">
  <button @click="updateToken">修改token</button>
</template>

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

  const store = useStore();
  const newtoken = ref("");

  //读取state的时候需要加上模块的名字
  const token = computed(() => store.state.Login.token);

  //读取getters时候不需要加上模块的名字
  const currentToken = computed(() => {
    return store.getters.getToken
  })

function updateToken(){
    store.dispatch("asyncUpdateToken",newtoken.value);
}
</script>

Module-命名空间

简单来说,Vuex 默认是全局的,也就是说同一个 actionmutationgetter 名字不能在不同模块中重复,否则会发生冲突。

但是,你可以通过添加 namespaced: true 给模块加上“命名空间”,这样它的 actionmutationgetter 就不会和其他模块冲突了,就像给它们自动加了个前缀,限制在自己的模块内。

  • 如果不加命名空间(默认全局),不同模块中的 actionmutationgetter 名字不能重复,否则会报错。

  • 它们会“混在一起”,无法区分属于哪个模块。

例如下面的模块配置:

const moduleA = {
  namespaced: true,
  state: { count: 0 },
  mutations: {
    increment(state) {
      state.count++;
    }
  },
  actions: {
    incrementAction({ commit }) {
      commit('increment');
    }
  }
};
const moduleB = {
  namespaced: true,
  state: { count: 0 },
  mutations: {
    increment(state) {
      state.count++;
    }
  },
  actions: {
    incrementAction({ commit }) {
      commit('increment');
    }
  }
};

在全局访问时,你需要通过模块名区分:

store.commit('moduleA/increment');
store.commit('moduleB/increment');

store.dispatch('moduleA/incrementAction');
store.dispatch('moduleB/incrementAction');

注意state不存在命名空间的概念,因为在调用的时候就要加上模块的名称。

8、核心概念-Vuex项目结构

Vuex 并不限制你的代码结构。但是,它规定了一些需要遵守的规则:

  1. 应用层级的状态应该集中到单个 store 对象中
  2. 提交 mutation 是更改状态的唯一方法,并且这个过程是同步的
  3. 异步逻辑都应该封装到 action 里面

只要你遵守以上规则,如何组织代码随你便。如果你的 store 文件太大,只需将 action、mutation 和 getter 分割到单独的文件

对于大型应用,我们会希望把 Vuex 相关代码分割到模块中。下面是项目结构示例

├── index.html
├── main.js
├── api
│  └── ... # 抽取出API请求
├── components
│  ├── App.vue
│  └── ...
└── store
  ├── index.js      # 我们组装模块并导出 store 的地方
  ├── actions.js     # 根级别的 action
  ├── mutations.js    # 根级别的 mutation
  └── modules
    ├── cart.js    # 购物车模块
    └── products.js  # 产品模块
 

9、Vuex严格模式

开启严格模式,仅需在创建 store 的时候传入 strict: true

const store = createStore({
 strict: true
})

在严格模式下,无论何时发生了状态变更且不是由 mutation 函数引起的,将会抛出错误。这能保证所有的状态变更都能被调试工具跟踪到。

不要在发布环境下启用严格模式!严格模式会深度监测状态树来检测不合规的状态变更——请确保在发布环境下关闭严格模式,以避免性能损失

10、Vuex表单处理

如果Vuex中处理的是表单输入框的数据,并且需要双向数据绑定效果该如何实现呢?

<template>
  <h3>搜索</h3>
  <input v-model="message"> <!-- 输入框,绑定到 computed 属性 message -->
  <p>{
  
  { message }}</p> <!-- 显示输入框的值 -->
</template>

<script setup>
import { computed } from "vue" // 从 Vue 中引入 computed 函数
import { useStore } from "vuex" // 从 Vuex 中引入 useStore 钩子

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

// 定义 computed 属性 message
const message = computed({
  // 获取 Vuex 中的 message 状态
  get() {
    return store.state.message; // 从 store 的 state 中获取 message
  },
  // 设置 Vuex 中的 message 状态
  set(value) {
    store.commit('updateMessage', value); // 调用 mutation 更新 message
  }
});
</script>

  1. v-model="message":

    • 输入框的值与 message 双向绑定。

    • 当输入框的值发生变化时,messageset 方法会被触发。

    • message 的值发生变化时,输入框的值也会自动更新。

  2. computed 属性:

    • get 方法:从 Vuex 的 state 中获取 message 值。

    • set 方法:当输入框的值发生变化时,调用 Vuex 的 mutation (updateMessage) 更新 state

  3. Vuex 的 state:

    • store.state.message 是 Vuex 中的状态,存储了当前的 message 值。

    • 通过 store.commit 调用 mutation,可以更新 state


http://www.kler.cn/a/531682.html

相关文章:

  • NLP模型大对比:Transformer >Seq2Seq > LSTM > RNN > n-gram
  • Java设计模式:行为型模式→状态模式
  • gitea - fatal: Authentication failed
  • 记忆化搜索和动态规划 --最长回文子串为例
  • 康德哲学与自组织思想的渊源:从《判断力批判》到系统论的桥梁
  • 可被electron等调用的Qt截图-录屏工具【源码开放】
  • 【漫话机器学习系列】078.如何选择隐藏单元激活函数(How To Choose Hidden Unit Activation Functions)
  • MySQL与Python交互-08
  • Java | CompletableFuture详解
  • 网站快速收录:如何优化网站音频内容?
  • bypass hcaptcha、hcaptcha逆向
  • 基于深度学习的视觉检测小项目(十七) 用户管理后台的编程
  • 如何确认Linux嵌入式系统的触摸屏对应的是哪个设备文件(/dev/input/event1)?如何查看系统中所有的输入设备?输入设备的设备文件有什么特点?
  • Linux进阶——例行性工作
  • PDFBox 替代方案(以及何时考虑更换)
  • 测试工程师的DS使用指南
  • 栈(5题)
  • 并行计算、分布式计算与云计算:概念剖析与对比研究(表格对比)
  • 【hot100】刷题记录(12)-回文链表
  • DeepSeek 核心技术全景解析
  • 排序算法3
  • Heptagon 同步语言介绍
  • 基于kamailio开发一个voip管理系统需要实现的基础功能
  • 如何在5步内使用 Spring AI 和 OpenAI 的 DALL-E 3 生成图像
  • 顺序打印数字的进一步理解
  • M. Triangle Construction