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
javascript">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文件引入。
javascript">//1.引入vuex import {createStore} from "vuex"; //2.创建vuex对象 const store = createStore({//3.创建状态state:{count:10} }) export default store
javascript">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中的。
javascript">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
javascript">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
javascript">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 mutationthis.$store.commit('increment');},// 定义减少按钮的点击事件处理函数minHandler() {// 调用 Vuex 的 commit 方法,触发 decrement mutationthis.$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 mutationstore.commit("increment");
};// 定义减少按钮的点击事件处理函数
const minHandler = () => {// 调用 Vuex 的 commit 方法,触发 decrement mutationstore.commit("decrement");
};
</script>
Mutation-携带参数
你可以向 store.commit
传入额外的参数,即 mutation 的载荷(payload)。
vuex配置
javascript">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传递参数
javascript"><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传递参数
javascript"><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获取
对象风格的传参方式
选项式
javascript"> methods:{addHandler(){this.$store.commit({type: 'increment',num: this.num});},minHandler(){this.$store.commit({type: 'decrement',num: this.num});}}
组合式
javascript">const addHandler = () => {store.commit({type: 'increment',num: this.num});
}
const minHandler = () => {store.commit({type: 'decrement',num: this.num});
}
注意:
此时在mutation 中获取的num参数要变为{num}
javascript"> 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
)
javascript"><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
javascript">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和解构后的numincrement(state, { num }) {state.count += Number(num); // 将state中的count值加上num的数值},// 定义一个名为decrement的Mutation,接收state和解构后的numdecrement(state, { num }) {state.count -= Number(num); // 将state中的count值减去num的数值}},// 定义Action:用于处理异步操作,可以提交Mutation来更改stateactions: {// 定义一个名为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
javascript"><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
javascript"><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-异步操作
安装依赖
javascript">npm install --save axios
实现异步Action
javascript">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.bannerstate.banner = banner;}},// 定义 Action:用于处理异步操作,可以提交 Mutation 来更改 stateactions: {// 定义一个名为 asyncSetBanner 的 Action,接收 context 和 url 参数asyncSetBanner(context, url) {// 使用 Axios 发送 GET 请求到指定的 URLaxios.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组件
javascript"><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使用。
javascript"><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文件
javascript">export default {state:{token:"asdasdlhcHDIHIO"}
}
在store下创建Order文件夹并在文件夹下创建index.js文件
javascript">export default {state:{order:[{id:101,name:"佳洁士"},{id:102,name: "舒肤佳"}]}}
在store主文件引入模块
javascript">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数据
javascript"><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数据
javascript"><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模块举例子
javascript">export default {state: {token: "asdasdlhcHDIHIO" // 定义初始状态,存储 token 值},// rootState: 顶级的 state,可以访问其他 module 或根 Vuex store 的 stategetters: {getToken(state, getters, rootState) {// 定义一个 getter,用于格式化 token 的输出// state: 当前 module 的 state// getters: 当前 module 的其他 getter// rootState: 整个 Vuex store 的根 state(如果需要访问其他 module 的 state)return "当前的 token 为: " + state.token; // 返回格式化后的 token 文本}},// 修改 statemutations: {updateToken(state, token) {// 定义一个 mutation,用于更新 state 中的 token 值// state: 当前 module 的 state// token: 传入的新的 token 值state.token = token; // 将传入的 token 值赋值给 state.token}},// 调用 mutations// rootState: 主文件中的根 stateactions: {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 默认是全局的,也就是说同一个 action
、mutation
或 getter
名字不能在不同模块中重复,否则会发生冲突。
但是,你可以通过添加 namespaced: true
给模块加上“命名空间”,这样它的 action
、mutation
和 getter
就不会和其他模块冲突了,就像给它们自动加了个前缀,限制在自己的模块内。
-
如果不加命名空间(默认全局),不同模块中的
action
、mutation
和getter
名字不能重复,否则会报错。 -
它们会“混在一起”,无法区分属于哪个模块。
例如下面的模块配置:
javascript">const moduleA = {namespaced: true,state: { count: 0 },mutations: {increment(state) {state.count++;}},actions: {incrementAction({ commit }) {commit('increment');}}
};
javascript">const moduleB = {namespaced: true,state: { count: 0 },mutations: {increment(state) {state.count++;}},actions: {incrementAction({ commit }) {commit('increment');}}
};
在全局访问时,你需要通过模块名区分:
javascript">store.commit('moduleA/increment');
store.commit('moduleB/increment');store.dispatch('moduleA/incrementAction');
store.dispatch('moduleB/incrementAction');
注意state不存在命名空间的概念,因为在调用的时候就要加上模块的名称。
8、核心概念-Vuex项目结构
Vuex 并不限制你的代码结构。但是,它规定了一些需要遵守的规则:
- 应用层级的状态应该集中到单个 store 对象中
- 提交 mutation 是更改状态的唯一方法,并且这个过程是同步的
- 异步逻辑都应该封装到 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
javascript">const store = createStore({strict: true
})
在严格模式下,无论何时发生了状态变更且不是由 mutation 函数引起的,将会抛出错误。这能保证所有的状态变更都能被调试工具跟踪到。
不要在发布环境下启用严格模式!严格模式会深度监测状态树来检测不合规的状态变更——请确保在发布环境下关闭严格模式,以避免性能损失
10、Vuex表单处理
如果Vuex中处理的是表单输入框的数据,并且需要双向数据绑定效果该如何实现呢?
javascript"><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>
v-model="message"
:
输入框的值与
message
双向绑定。当输入框的值发生变化时,
message
的set
方法会被触发。当
message
的值发生变化时,输入框的值也会自动更新。
computed
属性:
get
方法:从 Vuex 的state
中获取message
值。
set
方法:当输入框的值发生变化时,调用 Vuex 的mutation
(updateMessage
) 更新state
。Vuex 的
state
:
store.state.message
是 Vuex 中的状态,存储了当前的message
值。通过
store.commit
调用mutation
,可以更新state
。