• day46((VueJS)vuex(全局状态管理对象))


    一.vuex的配置步骤

    要点总结

    1. 下载指令:npm i vuex
    2. 创建 store 文件夹,在文件夹中创建 js 文件,用来存放 vuex 的配置;
    3. 在 js 文件中导入 vuex 中的createStore方法,调用该方法,实例化 vuex , 创建一个仓库对象( 这个对象是整个应用程序共享的,唯一的 );
    4. createStore方法调用时接受一个对象作为参数,在对象中,我们需要添加以下子对象:
      1) state:负责存储数据(状态)
      2) getters:定义计算方法
      3) mutations:定义同步方法
      4) actions:定义异步方法
      5) modules:以模块化的方式管理 state 中的状态
      6) plugins:配合其他第三方插件管理 state 中的状态
    5. 将实例化对象使用默认导出,然后导入入口文件 main.js,在该文件中使用vue应用实例.use()方法注册该全局状态管理对象,也称仓库对象或者 store 对象(在应用实例进行注册之后,任何一个组件内, 都可以通过组件实例(this)获取到该全局状态管理对象, 也就是 this.$store,但是要注意区分组件文件和非组件文件,有时候我们需要在一些 js 文件中使用到该仓库对象,这样的话就不能使用 this 直接打点调用了,这个时候就需要先把仓库对象实例化的文件使用默认导入才可以使用仓库对象)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    代码解析

    store/index.js
    
    import { createStore} from 'vuex';-----------从vuex中导入createStore()方法,预备仓库对象实例化使用
    export default createStore({实例化vuex , 创建一个仓库对象( 这个对象是整个应用程序共享的,唯一的 ),并使用默认导出,将该仓库对象导出
        state:{负责存储数据(状态)},
        getters:{ 定义计算方法},
        mutations:{定义同步方法}
        actions:{定义异步方法},
        modules:{以模块化的方式管理state中的状态},
        plugins:[配合其他第三方插件管理state中的状态]
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    二.同步方法

    要点总结

    同步方法就是 mutation 对象中定义的方法::
    
    1. 同步方法的特性:同步方法内部可以通过第一个参数直接修改 state 中的数据。(想要修改 state 中的数据,必须使用同步方法,不能使用点运算符获取进行修改,如果使用点运算符获取进行修改,则不会触发持久化之后自动同步localStorage的特性,也就是说localStorage中的属性是没有变化的,一刷新vuex实例的数据重新加载,自动将localstorage中的数据又同步到vuex实例中,修改的数据就还是老样子。)
    2. 同步方法的参数:
      参数一:是 state 本身,这个参数是固定的,所有的同步方法第一个参数都是 state,无法修改,调用该方法时,我们也只能传入第二个参数。
      参数二:调用该同步方式时传来的参数,格式是对象格式。
    3. 同步方法的调用
      同步方法在调用时,不能直接使用_mutation属性,而要使用仓库对象中的commit方法进行触发(调用)
      语法:仓库对象.commit("调用的同步方法名",传入对象格式的参数)
    4. 注意点:
      - 所有同步方法第一个参数都是 state,这是系统自动传入的参数,不需要调用时手动传参
      - 保存数据(登录数据)应该在路由跳转之前
      - 获取数据应该在保存数据之后(主要是注意各个组件生命周期函数的执行顺序)
      - 不同方法操作不同的数据,一般是一个方法操作一条数据(state 的直接子项)
      - 仓库对象不能通过点运算符直接修改数据,修改数据必须使用同步方法
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    代码解析

    同步方法定义:配置文件.js
    import { createStore } from 'vuex';从vuex中导入createStore()方法,预备仓库对象实例化使用
    
    export default createStore({-------实例化vuex , 创建一个仓库对象( 这个对象是整个应用程序共享的,唯一的 )
        state:{ -----------------负责存储数据(状态)
            loginData:{},------------提前设立对象储存数据
        },
        mutations:{ ------------定义同步方法, 同步方法内部可以通过第一个参数 直接修改state中的数据, 参数一:state本身, 参数二: 调用该同步方式时传来的参数
            saveLoginData(state,payload){ ----------------保存登陆返回的数据
                state.loginData = payload;----------只有在同步方法里面可以直接修改state里面的数据
            },
        },
    });
    
    同步方法调用:组件.vue
    <script>
    export default {
      data(){
        return {
          loginFormData:{
            loginname:'',
            password:''
          }
        }
      },
      methods:{
        handleLogin(){
          this.$store.commit('saveLoginData',{token:xxxxxx,id:xxxxxxx,.....})-----------调用同步方法,传入登录操作返回的数据以保存在仓库对象中,注意,该方法需要在路由跳转之前调用,跳转之后组件卸载,返回数据就拿不到了 
        }
      }
    }
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    三.异步方法

    要点总结

    异步方法全部在对象 actions 中定义。
    1. 异步方法的特性:异步方法内部负责执行异步代码( 发请求, 定时器 ), 不可以直接修改 state 中的数据, 只能间接调用同步方法修改 state 中的数据
    2. 异步方法的参数
      1) 参数一:store 实例(与同步方法中的第一个参数一样,系统自动传参,是固定的,所有的异步方法都是这样)
      2) 参数二: 调用该异步方式时传来的参数(格式是对象格式)
    3. 异步方法的调用
      1) 异步方法也是不能直接调用的,需要使用仓库对象中的dispatch方法调用
      2) 语法:
        仓库对象.dispatch("异步参数名",调用时传入的参数)
    应用场景:一般在需要先进行请求的时候调用异步方法(存在先进行定时器,发请求等异步操作时需要调用异步方法)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    代码解析

    import { createStore,createLogger } from 'vuex'
    import * as service from '../api';-------------集中导入封装好的请求函数方便使用
    import { showSuccessToast,showFailToast } from 'vant';---------------命名导入导入第三方插件vant中的两个弹窗提示的方法,方便调用
    import "vant/es/toast/style";----------------导入这两个弹窗提示的方法需要的样式文件;
    import router from '../router';------------------导入路由管理对象,方便在这里跳路由
    
    在组件内获取仓库对象,使用:this.$store
    在组件外获取仓库对象,必须先导入再使用
    但这个文档是仓库对象实例化的文档,所以该文档不是组件但也不需要导入,使用仓库对象的特定名称store就可以直接调用仓库对象的方法
    
    export default createStore({---------------实例化vuex , 创建一个仓库对象( 这个对象是整个应用程序共享的,唯一的 )
        state:{ ---------------------------负责存储数据(状态)
            loginData:{},
        },
        actions:{ ----------------------------------定义异步方法
            async requestLogin( store, loginFormData){
                var res = await service.user_login( loginFormData )-------------------发起登陆请求
                if( res.data.code == 200 ){----------------登陆成功, 跳转到之前的页面( 默认是首页 )
                    showSuccessToast({ message: res.data.message })-------------弹出"登陆成功"提示
                    
                    store.commit('saveLoginData',res.data.data)--------调用vuex中的同步方法, 保存登陆接口返回的数据,这个文档是仓库对象实例化的文档,所以该文档不是组件但也不需要导入,使用仓库对象的特定名称store就可以直接调用仓库对象的方法
    
                    router.push('/index/home');------在组件外访问路由需要先导入路由管理对象实例化的文件,然后才能使用,在组件内访问路由只需要使用组件实例this打点调用即可,进位路由管理对象是在main.js中使用vue应用实例注册过的
    
                }else{-------------------------------------------登陆失败,弹框提示 
                    showFailToast({ message: res.data.message,icon:'close' });-----与showSuccessToast()都是从第三方插件vant中引入的方法,在引入这些方法时也需要同时将其样式文件导入。
                }
            }
        }, 
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    四.同步和异步方法的区别

    要点总结

    同步方法可以通过其第一个参数 state 修改 state 数据,它属于可以直接修改数据的方法
    异步方法不能直接修改数据,只能在其中调用同步方法间接修改数据
    组件内使用同步还是异步方法,主要取决于是否有先进行异步操作(发请求)再修改数据的需求
    
    • 1
    • 2
    • 3

    五.组件内外获取路由管理对象的方法

    要点总结

    组件内:通过当前组件实例 this 直接打点调用:this.$router
    组件外:先导入路由管理对象实例化的文档,然后调用
    有时候可能需要在实例化路由管理对象的文档里面使用该对象做一些操作,比如当添加全局的路由守卫函数时,我们需要使用该对象调用 beforeEach方法来调用,这个时候就不需要导入任何文件了,直接使用实例化的对象名即可
    
    • 1
    • 2
    • 3

    六.组件内外获取仓库对象的方法

    要点总结

    组件内:通过当前组件实例 this 直接打点调用:this.$router
    组件外:先导入路由管理对象实例化的文档,然后调用
    有时候可能需要在实例化路由管理对象的文档里面使用该对象做一些操作,比如当添加全局的路由守卫函数时,我们需要使用该对象调用 beforeEach方法来调用,这个时候就不需要导入任何文件了,直接使用实例化的对象名即可
    
    • 1
    • 2
    • 3

    七.store数据的使用(鉴权与发请求时的token)

    要点总结

    1. 鉴权
    鉴权操作在前置路由守卫函数中进行,可以是全局路由前置守卫函数或者路由独享的守卫函数,二者各自有不同的生效范围
    步骤与思路:在前置路由守卫函数中获取存储在仓库对象中的 token,如果仓库中存在令牌 token,证明用户已登录,是有权限的,所以可以访问跳转的目标页面;如果不存在 token,证明用户未登录,则直接跳转登录页面
    由于前置路由守卫函数一般被定义在 js 文档之中(全局守卫函数一般在路由管理对象实例化的 js 文档之中,路由独享守卫函数则被定义在路由配置表中,这个表一般也会被抽离到单独的 js 文件中方便统一管理),并不在组件之中,所以我们在使用仓库对象是,需要先导入仓库对象实例化的文档,然后再进行调用
    2. 发请求
    当我们在发请求需要携带 token 时,可以再路由管理对象的请求拦截器中添加一些普遍需要的 token,在这里添加的 token 是全局的,每一种请求每一次发出都会携带该 token
    ps:此处 token 只是获取仓库数据应用场景的举例,鉴权与发请求时携带的请求头数据不止 token 一种
    ps:304 状态代表本次请求的页面或者结果来自于浏览器缓存,而不是来自于服务器的响应
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    代码解析

    仓库数据获取应用与路由守卫函数中的鉴权
    import store from '../store';-------------这里需要导入仓库对象实例化的文档才能调用仓库对象
    const router = createRouter({
      history: createWebHistory(import.meta.env.BASE_URL),
      routes
    })
    router.beforeEach((to,from,next)=>{
    鉴权( 有访问当前路由的权限,则可以访问, 否则不能访问 )
      var token = store.state.loginData.token;---------通过仓库对象打点获取state里面登录返回信息里面的token,作为用户已登录的判断条件
      if( to.meta.requireAuth ){ ---------------------------------获取每个路由对象专属的元信息,如果元信息中存在需要登录的条件,则代表该路由对象指向的组件或者是页面需要登录才能访问 
        if( token ){------------------------如果仓库对象中存在token令牌,证明用户已登录,放行即可
          next();------------------------允许路由跳转
        }else{
          next('/login');
        }
      }else{ -------------------------不需要登录 
        next();--------------------------允许路由跳转
      }
    })
    export default router;
    发请求时的仓库对象应用
    import axios from 'axios'
    import { Toast,showFailToast } from 'vant';
    import { showNotify   } from 'vant';
    import 'vant/es/toast/style';
    import 'vant/es/notify/style';
    import store from '../store'
    
    var service = axios.create({
        timeout:10*1000,
        baseURL:'/'
    })
    
    service.interceptors.request.use(
        (config)=>{
            config.headers['token'] = store.state.loginData.token;-------------添加请求头参数,此处使用导入的仓库对象获取token并将其设置为请求头,这里设置的请求头是全局的,所有的请求都会写带上这里的请求头。
            return config;
        },
        (error)=>{
            return Promise.reject(error);
        },
    )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    八.store数据的持久化

    要点总结

    关于仓库对象应用时存在的问题
    1. 表现:刷新页面之后存储在其中的数据就会丢失,从而使得需要判断数据的鉴权,需要携带请求头的请求无法进行。
    2. 原理:我们在对仓库对象实例化的过程中会将仓库对象实例化的文档导入入口文件main.js中进行注册,而入口文件又被引入到页面文件index.html中,在页面刷新时,该页面文件会被影响刷新,此时导入其中的入口文件也被影响重新导入并重新执行入口文件中所有的代码都会重新执行,包括引入其中的仓库对象也重新导入,导入时文档中的代码再次执行,实例化了一个新的仓库对象,所以之前仓库中存进去的状态全部被重置,所以会出现数据丢失问题。
      1) 简单解释:在页面刷新时,Vue.js 应用程序会重新加载,因此会重新创建 Vuex 实例。由于没有使用持久化插件,因此在创建 Vuex 实例时,state 会被重新初始化,所有之前的状态都会丢失。
    3. 解决方案:使用持久化插件(例如:vuex-presistedstate插件,该插件使 vuex 实例化仓库对象中数据能够保留下来的原理就是将所有数据作为一个整合对象存入 localStorage 中,使得数据在本地存储中保留。也就其他插件比如vue-along的原理是将数据存储在 cookie 中,相比之下更推荐前者,因为 cookie 的存储空间只有 4k,但 localStorage 的存储空间有 4M-5M,能存储更多的数据)
    4. 步骤:
      1) 插件下载指令:npm install vuex-persistedstate
      2) 在仓库对象实例化的文档中导入该插件
      3) 在仓库对象的 plugins 中添加插件(添加之后自动生效,能够监听/发现数据变化并自动同步到 localStorage 中)
    自动化打印日志:该功能通过仓库对象的方法 createLogger 生效,需要先从 vuex 中导入该方法然后在 plugins 中调用,就可以在每次检测到路由跳转后自动执行
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    代码解析

    import { createStore,createLogger } from 'vuex';--------从vuex中导入createLogger方法,该方法功能位在每次跳转之后打印日志
    import { createVuexPersistedState  } from 'vue-persistedstate';----------导入持久化插件
    export default createStore({
        plugins:[ -------------------------配合其他第三方插件管理state中的状态
            createLogger(),-------------------------自动化打印日志
            createVuexPersistedState({----------------------state的持久化存储插件设置
                key:'vuex',
                storage: window.localStorage,-------------该语句指定数据存储在localStorage中还是sessionStorage中,一般推荐localStorage,sessionStorage时会话级别的生命周期,页面关闭就自动销毁了,大多数数据需要一定的保存时长。
    
                whiteList:[],------------白名单,需要持久化存储的数据
                blackList:[],------------黑名单,不需要持久化存储的数据
            })
        ] 
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    九.计算方法

    要点总结

    计算方法( 类似于组件中的计算属性 ),定义时像函数,使用时像变量
    
    • 1

    代码解析

    import { createStore } from 'vuex'
    export default createStore({------------实例化vuex
        state:{ --------------------存储数据
            tasklist:[ /* { id:1, name:'', status:false } */ ]
        },
        getters:{ --------------------定义计算方法( 类似于组件中的计算属性 )
            totalCount(state){--------------统计任务总数
                return state.tasklist.length;
            },
            finishedCount(state){-----------------统计已完成任务数量
                return state.tasklist.filter(item=>item.status).length;
            },
            unfinishedCount(state){-------------统计未完成任务数量
                return state.tasklist.filter(item=>!item.status).length;
            }
        },
        mutations:{ ---------------------定义同步方法
            addTask( state, payload ){-----------新增任务
                if( payload ){
                    var obj = { id: state.tasklist.length + 1 ,name: payload, status:false  };-----构造一个新的任务对象
    
                    state.tasklist.push( obj );-------添加新的任务对象 到任务列表中
                }
            },
            changeTaskStatus( state, payload ){------------更改任务状态
                var task = state.tasklist.find( item=>item.id == payload );------根据id 找到需要更改状态的任务对象
                if( task ){------------------更改任务对象的状态
                    task.status = !task.status;
                }
                console.log( task,payload );
            },
            deleteTask( state, payload ){-------------删除任务
                var i = state.tasklist.findIndex(item=>item.id == payload);------根据id找到 需要删除的元素位置(下标)
                if( i != -1 ){
                    state.tasklist.splice(i,1);----------删除对应位置的任务对象
                }
            }
        },
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
  • 相关阅读:
    快速记忆23种设计模式
    复习一下Linux常用命令,孰能生巧~
    sql各种注入案例
    Python手写贝叶斯网络
    【Spring Cloud】深入探索 Nacos 注册中心的原理,服务的注册与发现,服务分层模型,负载均衡策略,微服务的权重设置,环境隔离
    基于SpringBoot和PotsGIS的各省地震震发可视化分析
    gin实现event stream
    解锁ESP32-C3 精简版的 GPIO11
    【LGR114C】地地铁铁
    c语言进阶篇:指针(五)
  • 原文地址:https://blog.csdn.net/weixin_69145757/article/details/136372598