目录
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
Vuex是一个状态管理模式,也叫状态机,将组件共享的数据放到状态机中统一管理,组件想拿数据可以从自己的数据模型中拿,也可以从仓库中拿,可以把vuex理解成一个仓库。
假设A组件想要拿B组件里面的数据,那就把A组件和B组件的数据放到Vuex中,A组件想要获取B组件数据从vuex中拿,B组件想要获取A组件的数据也从Vuex拿。
- {
- //state 维护的是公共状态(数据),类似Vue中的data
- state: {},
- //getters 处理state中的数据并返回新数据,类似计算属性computed
- getters: {},
- //mutations 突变,修改state的唯一方式,只能做同步操作
- mutations: {},
- //actions 动作,处理异步操作,获取后台响应,提交数据给突变mutations
- actions: {},
- // 状态机可以有多个,modules用来存放各模块的状态机
- modules: {}
- }
vuex三个主要的属性:state、mutations、actions
当我们声明了一个状态机并将其注入Vue实例中,那么Vue实例上就会存在一个$store属性,该属性上有dispatch和commit等方法。
分析上图:
Vue组件将需要执行的方法通过dispatch方法派发给Actions执行,但是Actions不会去执行该方法,于是它又通过commit方法提交该方法给Mutation来执行,Mutation执行完后,将处理完的数据给State,然后State重新渲染数据到页面。
注意:当Vue组件需要操作的数据不需要通过网络请求得到时,可以跳过actions,直接使用commit方法提交给mutations处理。
1、通过CDN引入Vuex
Vuex是Vue的一个插件,使用时需要安装
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.6.0/vuex.min.js" integrity="sha512-uN93RUcJ9frHH6dyLknjgalFe7JNkfb3OjW4Qgg5xjaVA3U7l0diZ3hGL2Puk/38sp7xD/SLHdNFit7Kq3RbtQ==" crossorigin="anonymous" referrerpolicy="no-referrer">script>
2、声明状态机
- let store = new Vuex.Store(
- {
- //state 维护的是公共状态,类似Vue中的data
- state: {},
- //getters 处理state中的数据并返回新数据,类似计算属性computed
- getters: {},
- //mutations 突变,修改state的唯一方式,只能做同步操作
- mutations: {},
- //actions 动作,处理异步操作,获取后台响应,提交数据给突变mutations
- actions: {}
- }
- )
3、将状态机注入Vue实例
- let vm = new Vue({
- // 将状态机注入Vue实例
- store,
- el: "#app",
- data: {}
- })
此时可以打印Vue实例中的$store看一下:
console.log(vm.$store)
因此我们可以在插值语法中使用 $store.state.xxx 和 $store.getters.xxx 访问state中的数据和getters中的方法。
例子:
- html>
- <html lang="en">
-
- <head>
- <meta charset="UTF-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Documenttitle>
- <script src="https://cdn.jsdelivr.net/npm/vue@2.7.10">script>
-
- <script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.6.0/vuex.min.js" integrity="sha512-uN93RUcJ9frHH6dyLknjgalFe7JNkfb3OjW4Qgg5xjaVA3U7l0diZ3hGL2Puk/38sp7xD/SLHdNFit7Kq3RbtQ==" crossorigin="anonymous" referrerpolicy="no-referrer">script>
- head>
-
- <body>
- <div id="app">
-
- {{$store.state.msg}}
- {{$store.getters.MSG}}
- <hr>
- {{$store.state.sum}}
- <button @click="foo">点击加{{n}}button>
- div>
- <script>
- let store = new Vuex.Store({
- state: {
- msg: 'hello',
- sum: 0
- },
- // getters用来处理state中的数据
- getters: {
- MSG(state){ //默认有一个参数,它是一个对象,里面存放了state中的数据
- // console.log(state);
- return state.msg.toUpperCase()
- }
- },
- actions: {
- // 默认有两个参数:sto是一个类状态机对象; value是dispatch传递过来的数据
- add(sto, value){
- // console.log(sto, value, 'actions');
- // 提交突变给mutations
- // commit('mutations中的方法名', 需要传递的数据)
- sto.commit('ADD', value)
- }
- },
- mutations: {
- // 最终在mutations中处理数据
- // 默认两个参数:state是一个对象,里面存放了state中的数据; value是commit提交的时候传递过来的数据
- ADD(state, value){
- // console.log(state, value, 'mutations');
- state.sum += value
- }
- },
- })
-
- let vm = new Vue({
- store,
- el: "#app",
- data: {
- n: 2
- },
- methods:{
- foo(){
- // 派发一个动作给actions
- // dispatch('actions中的方法名', 需要传递的数据)
- this.$store.dispatch('add', this.n)
- }
- }
- })
- // console.log(vm); //打印Vue实例,可以看到Vue实例上有一个$store属性
- script>
- body>
-
- html>
Vuex提供的组件辅助函数有mapState、mapGetters、mapActions、mapMutations,它们存在Vuex对象上,使用时可以使用Vuex.mapState的方式调用,也可以将它们从Vuex中解构出来使用:
let { mapState, mapGetters, mapActions, mapMutations } = Vuex
1、mapState方法:用于帮助映射 state 中的数据为计算属性
- computed:{
- // msg(){
- // return this.$store.state.msg
- // },
- // sum(){
- // return this.$store.state.sum
- // }
- //-----------------------------------------------------
- // 借助mapState函数生成上述计算属性
- // mapState返回一个对象,我们需要使用扩展运算符将它们展开
- // 1、对象写法
- ...mapState({msg:'msg',sum:'sum'})
- // 2、数组写法
- ...mapState(['msg','sum'])
- },
- // 测试,看一下mapState返回值
- created(){
- let a = mapState({msg:'msg',sum:'sum'})
- console.log(a); //返回一个对象,对象中有msg、sum方法
- },
使用了mapState方法之后,我们在插值语法直接使用{{msg}}、{{sum}}即可,不需要写成:{{$store.state.msg}}、{{$store.state.sum}},同理mapGetters方法也一样。
2、mapGetters方法:用于帮助映射 getters 中的数据为计算属性
- computed:{
- // MSG(){
- // return this.$store.getters.MSG
- // }
- //------------------------------------------------------
- // 借助mapGetters函数生成上述计算属性
- // 1、对象写法
- ...mapGetters({MSG:'MSG'})
- // 2、数组写法
- ...mapGetters(['MSG'])
- },
3、mapActions方法:用于帮助生成与 actions 对话的方法,即包含$store.dispatch(xxx)的函数
- methods:{
- // foo(){
- // this.$store.dispatch('add', this.n)
- // }
- //--------------------------------------------
- // 借助mapActions函数生成上述foo方法
- // mapActions返回一个对象,我们需要使用扩展运算符将它们展开
- // 1、对象写法
- ...mapActions({foo:'add'}),
- // 2、数组写法 (注意:需要对象的属性名和属性值一样才能写成下面的数组形式)
- ...mapActions(['foo'])
- }
4、mapMutations方法:用于帮助生成与 mutations 对话的方法,即包含$store.commit(xxx)的函数
- methods:{
- // foo(){
- // this.$store.commit('ADD', this.n)
- // }
- //--------------------------------------------
- // 借助mapMutations函数生成上述foo方法
- // mapMutations返回一个对象,我们需要使用扩展运算符将它们展开
- // 1、对象写法
- ...mapMutations({foo:'ADD'}),
- // 2、数组写法 (注意:需要对象的属性名和属性值一样才能写成下面的数组形式)
- ...mapMutations(['foo'])
-
- }
注意:使用 mapActions 和 mapMutations 时,若需要传递参数,那么参数要在模板绑定事件的时候传参。
<button @click="foo(n)">点击加{{n}}button>
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。为了解决以上问题,Vuex 允许我们将 store 分割成模块,每个模块拥有自己的 state、mutation、action、getter,甚至是嵌套子模块。namespaced表示设置命名空间。
Vuex中的modules属性用来存放各个模块的状态机。
- let aModule = {
- // 开启命名空间
- namespaced: true,
- state:{},
- getters:{},
- actions:{},
- mutations:{}
- }
- let bModule = {
- namespaced: true,
- state:{},
- getters:{},
- actions:{},
- mutations:{}
- }
- let store = new Vuex.Store({
- modules:{
- a:aModule,
- b:bModules
- }
- })
(1)组件中读取state中数据
- // 方式一:自己读取
- this.$store.state.a.xxx
- // 方式二:使用mapState读取 表示后面数组里面的属性是a模块的
- ...mapState('a',['xxx1','xxx2','xxx3'])
(2)组件中读取getters中的数据
- // 方式一:自己读取
- this.$store.getters['a/xxx']
- // 方式二:使用mapGetters读取 表示后面数组里面的属性是a模块的
- ...mapGetters('a',['xxx1','xxx2','xxx3'])
(3)组件中调用dispatch
- // 方式一:自己直接dispatch
- this.$store.dispatch('a/xxx',需要传递的数据)
- // 方式二:使用mapActions读取
- ...mapActions('a',['xxx1','xxx2','xxx3'])
(4)组件中调用commit
- // 方式一:自己直接commit
- this.$store.commit('a/xxx',需要传递的数据)
- // 方式二:使用mapMutations读取
- ...mapMutations('a',['xxx1','xxx2','xxx3'])
写个小案例:当我们点击登录的时候会获取到一个token值,这个token值其他组件也需要使用,因此我们可以把token值存在vuex中,供所有的组件使用。
1、安装vuex
注意:vuex4版本以上只能在vue3中使用,vue2使用vuex3版本,安装时如果不指定版本,会安装最新版本的vuex
npm i vuex@3.6.2
2、在src下新建一个文件夹store,然后在store文件夹下再新建一个文件夹login,存放登录相关的状态机
src/store/index.js
- import Vue from 'vue';
- import Vuex from 'vuex';
- // 引入登录状态机
- import login from '../store/login/login';
-
- Vue.use(Vuex);
-
- export default new Vuex.Store({
- state:{},
- getters:{},
- mutations:{},
- actions:{},
- modules:{
- login
- }
- })
src/login/login.js
- import axios from 'axios';
- // 这是我登录状态机配置对象
- export default {
- // 开启命名空间
- namespaced: true,
- state: {
- token: localStorage.getItem('token') || ""
- },
- gettes: {},
- mutations: {
- SET_TOKEN(state, payload) {
- state.token = payload;
- // 持久化储存
- localStorage.setItem('token', payload)
- }
- },
- actions: {
- async login({commit}, payload) {
- // 发送异步请求
- let res = await axios.post('登录接口地址', payload)
- // console.log(res);
- commit('SET_TOKEN', res.data.data.token)
- }
- }
- }
3、在src下新建文件夹pages,存放组件
login.vue
- <div>
- 登录页面-----
- <button @click='login(obj)'>登录button>
- div>
- <script>
- import {mapActions} from 'vuex'
- export default {
- data(){
- return {
- obj:{
- username:"xxx",
- password:xxx
- }
- }
- },
- methods:{
- // 分发状态机动作 发送登录请求
- ...mapActions('login',['login'])
- }
- }
- script>
user.vue
- <div>
- 用户页面--{{token}}
- div>
- <script>
- import {mapState} from 'vuex';
- export default {
- data(){
- return {}
- },
- computed:{
- ...mapState('login',['token'])
- }
- }
- script>