状态管理库(Vuex&Pinia)

Vuex

概述

vuex是事项组件全局状态(数据)管理的一种机制,可以方便实现组件之间数据的共享

好处:

1、集中管理共享的数据,抑郁开发和后期维护

2、高效的实现组件之间的数据共享,提高开发效率

3、存储的数据都是响应式的,能够实时保持数据与页面的同步

存储数据类型:组件之间共享的数据才有必要存储到vuex中,对于组件中的私有数据,依旧存储在自身的data中即可

安装

配置vue项目

安装依赖包:

npm install vuex --save

导入vuex包:

import Vuex from 'vuex'

Vue.use(vuex)

创建store对象:

const store=new Vuex.Store({
  //state中存放的就是全局共享的数据
  state:{count:0}
})

挂载store对象到vue实例中

new Vue({
el:'#app',
render:h=>h(app),
router
//将创建的共享数据对象,挂载到vue实例中
//所有的组件直接从store中获取全局的数据
store
})

基本语法

基本操作

大致框架:

store/index.js

import {createStore}from 'vuex'
export default createStore({
state:{
//数据
},
getters:{
//普通方法,要有return
//注:getters只是基于 state 的派生状态,不会改变原始的 state 数据,只是对数据进行加工处理。
},
mutations:{
//计算方法,其中的方法形参可以为(context,payload),context表示上下文对象包括commit,payload表示调用这个函数的时候给的参数
//调用mutations中的函数需要state.commit('函数名',payload(调用这个函数时传回的参数))
},
actions:{
//用来包装mutations中函数的异步操作,直接在异步操作中调用context.commit('函数名',payload)
},
modules:{
}
})

App.vue

<template>
<div>
  <span>{{$store.state.属性名}}</span>
<p>{{$store.getters.方法名}}</p>
<p>{{$store.getters['方法名']}}</p>
 <button @click="mutaionsFn">mutations</button>
     <button @click="actionFn">actions</button>
</template>
<script>
import {useStore}from 'vuex';
export default{
name:'App',
//记得要return方法
setup(){
const store=useStore();
const mutaionsFn=()=>{//调用mutations中的方法   store.commit('updateName')
    };
    //针对调用actions中的方法
    const actionFn=()=>{
      store.dispatch('increateAsync',6);
    }
    return{
      mutaionsFn,actionFn
    }
}
}

要点:

state/getter调用:$store.state/getter.属性名/方法名

getter调用还可以$store.getter['方法名']

mutations调用:store.commit('方法名',传回的参数名)//传回的参数可以为空

actions调用:

store.dispatch('方法名',传回的参数名)//传回的参数可以为空

模块

store/index.js

import {createStore}from 'vuex'
const moduleA={
state:{
//数据
},
getters:{
//getters方法(有return值)
},
};
const moduleB={
namespaced:true,
state:{
state:{
//数据
},
getters:{
//getters方法(有return值)
},
mutations:{
//计算方法
}
actions:{
//包装mutations中函数的异步操作
}
export defaule createStore({
modules: {
    moduleA,
    moduleB
  }
})
}

要点:

如果声明namespaced:true,则表示开启了命名空间,调用方法的时候可以用访问模块名来调用(eg:$store.getters['moduleB/newName']),

如果未声明,则调用的时候要直接访问对应的方法名(eg:$store.getters.newName),相当于全局方法

一定要用modules:{模块名}来注册模块

App.vue

<template>
  <div>
    <p>{{ $store.state.模块名.属性名}}</p>
//没有namespaced:true时的方法访问方式
    <p>{{ $store.getters.newName }}</p>
    <p>{{ $store.state.moduleB.username }}</p>
//有namespaced:true时的方法访问方式
    <p>{{ $store.getters['moduleB/newName'] }}</p>
    <button @click="mutationsFn">mutationsFn</button>
    <button @click="actionsFn">actionFn</button>
  </div>
</template>
<script>
import { useStore } from 'vuex';
 export default {
  name:'App',
  setup(){
    const store=useStore();
    const mutationsFn=()=>{
      store.commit('moduleB/方法名')
    };
    const actionsFn=()=>{
      store.dispatch('moduleB/方法名',传回参数);
    };
    return {
      mutationsFn,actionsFn
    };
  }

}
</script>

要点:

没有namespaced:true时的方法访问方式:$store.getters.newName

有namespaced:true时的方法访问方式:$store.getters['模块名/方法名']

调用mutations的方法时,要用store.commit('模块名/方法名')

调用actions的方法时,要用store.dispatch('模块名/方法名')

数据持久化

配置:npm i vuex-persistedstate

模块文件user.js:

export defaule {
namespaced:true,
state(){
return{
profile:{
id:'',
avatar:'',//头像 nickname:'',//昵称
account:'',//账号名称
mobile:'',//收集号码       token:'',//token数据
      }
    }
  },
mutations:{
setUser(state,payload){  state.profile=payload
    }
  }
}

store/index.js文件:

import {createStore}from 'vuex'
import createPersistedState from 'vuex-persistedstate'
import user from 'user.js'
modules:{
user,
},
plugins:[
createPersistedState({
key:'shop-project-vuex-store',
paths:['user']
})
]
})

App.vue文件:

<template>
  <div id="nav">
    {{ $store.state.user.profile.account }}
    <button @click="$store.commit('user/setUser',{account:'张三'})">更新用户账号名称</button>
  </div>
</template>
<script>
import { useStore } from 'vuex';
 export default {
  name:'App',
}
</script>

要点:

配置文件:npm i vuex-persistedstate

store文件:

import {createStore}from 'vuex'

import createPersistedState from 'vuex-persistedstate'

export default createStore({

  modules: {
  //模块名
  },
  plugins:[
    createPersistedState({
      key:'shop-project-vuex-store',
      paths:['需要持久化的模块名'],
    })
  ]
})

请求拦截器&响应拦截器

axios配置

import axios from 'axios';
import store from '@/store'//导入store文件
import router from '@/router'//导入路由文件
const baseURL='https://apipc-xiaotuxian-front.itheima.net/'
//自定义axios实例
const instance=axios.create({
  baseURL,//请求的基础URL
  timeout:5000//请求超时时间,如果超过指定时间没有响应,则会报错超时
})

请求拦截器(在每个请求发送之前进行预处理操作)

// 第一个参数:实现具体的拦截任务,config表示请求的配置信息(包括URL、headers、data等)
instance.interceptors.request.use(config=>{
//获取user模块里面state返回的所有用户信息
  const {profile}= store.state.user;
//检查用户信息是否能通过服务器校验
  if(profile.token){
 //将token字段添加到请求头中(这样就能在每次请求时自动带上用户的身份验证信息,确保请求能够通过服务器的身份验证。)
config.headers.Authorization=`Bearer ${profile.token}`;
  }
//返回修改后的配置对象,这样 axios 才会继续执行这个请求。
  return config;
//第二个参数:拦截失败后的操作
},err=>{
  return Promise.reject(err)
})

响应拦截器

instance.interceptors.response.use(res=>{
return res.data
},err=>{
//token数据无效(没有通过服务器校验)
if(err.response&&err.response.status===401){
//清空无效的用户信息
store.commit('user/setUser',{});
//跳转到登录页面(获取全部地址,以防结构不同导致无法获取id),在组件当中是$route.fullPath,在js文件中需要router.currentRoute.value.fullPath来获取(相当于一个ref对象,要用value来访问他的值)
    //如果用router.push(),则系统会自动解析url地址,如果地址中有诸如&的特殊符号,则只会读到这里,符号之后的字符读不到
const fullPsth=encodeURIComponent(router.currentRoute.value.fullPath)
//将所访问的当前路由信息传递到登录页面中,当用户登录成功以后直接跳转到以前所访问的页面
router.push('登录页面网址/reddirectUrl='+fullPath)//reddirectUrl是参数,用户登录以后就可以跳转到之前访问的URl地址
}
return Promise.reject(err)
})

请求函数封装

export default(url,method,submitData)=>{
return instance({
url,//请求的地址
method,//请求的方式(get,post,GET,POST)
[method.toLowerCase()==='get'?'params':'data']:submitData//判断method是get还是post(大写转为小写)然后将相应的参数传给服务端

请求测试

App.vue文件

<template>
  <div id="nav">
    <button @click="fn">测试请求</button>
  </div>
</template>
<script>
import request from //请求拦截器文件
export default{
name:'App',
setup(){
const fn=()={
request('//请求地址',请求方法(get/post),{请求参数})

网址呈现:当前网址=%2F+之前访问的网址

Pinia

基本概念:Pinia是Vue的专属的最新的状态管理库,它允许跨越组件或页面共享状态,是Vuex状态管理工具的替代品

优势:

提供更简单的API(去掉了mutation)

提供更符合组合式风格的API(和Vue3新语法统一)

去掉了modules的概念,每一个store都是一个独立的模块

搭配了TypeScript一起使用提供可靠的类型推断

配置:

配置Vue项目文件

npm install pinia

main.js文件:

// 导入pinia
import { createPinia } from 'pinia'
//执行方法得到实例
const pinia=createPinia()
//将实例加入到app应用中
createApp(App).use(pinia).mount('#app')

定义Store

store文件:

//引入pinia文件
import {defineStore}from 'pinia'
//定义方法
import {ref}from 'vue'
export const useCounterStore=defineStore('counter',()=>{
  const count=ref(0)
  const increment=()=>{
    count.value++
  }
  //getter定义
  const doubleCount=computed(()=>count.value*2)
  //定义异步action
  const list=ref([])
  const getList=async()=>{
   const res=await axios.get(API_URL)
   list.value=res.data.data.channels
  }
  // 要把所有的方法和数据全部return
  return {count,doubleCount,increment,list,getList}
})

App.vue文件:

<template>
  <button @click="counter.increment()">Count:{{ counter.count+1 }}</button>
  {{ counter.doubleCount }}
  <ul>
    <li v-for="item in couter.list" :key="item.id">{{ item.label }}</li>
  </ul>
</template>

<script setup>
  import {onMounted}from 'vue'
  import {storeToRefs} from 'pinia'
  import {useCounterStore}from '@/stores/counter'
  const counter=useCounterStore()
  onMounted(()=>{
    counter.getList()
  })
  // 直接解构赋值会让响应式丢失,所以要用storeToRefs包裹(保持响应式更新)
  //storeToRefs只会对store的数据包裹,不会管方法
  const {count.doubleCount}=storeToRefs(counter)
  //方法直接从 原来的counter中解构赋值
  const {increment}=counter
  // //+1
  // counter.count++
  // //自动补全+1
  // counter.$patch({count:counter.count+1 })
  // //pinia方法+1
  // counter.increment()
</script>

state 是 store 的数据 (data),getters 是 store 的计算属性 (computed),而 actions 则是方法 (methods),可以接受同步/异步。


状态管理库(Vuex&Pinia)
http://localhost:8090//archives/vuex
作者
林依琪
发布于
2025年03月03日
许可协议