vue-状态管理

272 阅读2分钟

状态管理

打算开发中大型应用,集中式数据管理, 一处修改,多处使用,多个组件依赖于同一状态,来自不同组件的行为需要变更同一状态,生态环境给我们提供了官方插件vuex

vuex相关成员

import Vuex from 'vuex', Vuex是个对象,相关成员如下

成员用途
Store类,构造状态管理的实例
mapActions函数,通讯工具
mapMutations函数,通讯工具
mapGetters函数,通讯工具
mapState函数,通讯工具

状态管理实例相关成员

成员用途
dispatch实例方法,通讯工具
commit实例方法,通讯工具
state属性,获取公共数据

角色分工

components->actions->mutations->state<-getters->->components
发送请求同步,异步业务修改状态状态读取处理状态渲染状态
学生代课老师校长财务班主任学生
dispatchcommitstate.key=value{key:value}state.key{{key}}
mapActions-><-mapGetters
mapMutations->-------------------><------------<-mapState
commit->------------------->
<------------<-state

交互流程图

vuex

配置

// /src/main.js

//引入store实例
import store from './plugins/vuex.js'

new Vue({
  render: h => h(App),
  store//控制vue实例,为vue实例提供一个状态管理实例,管理整个vue公共状态
}).$mount('#app')

// src/plugins/vuex.js

//配置 store
import Vue from 'vue';
import Vuex from 'vuex';//引入vuex包
Vue.use(Vuex);//安装插件到vue

import state from '../store/state.js'
import actions from '../store/actions.js'
import mutations from '../store/mutations.js'
import getters from '../store/getters.js'

let store = new Vuex.Store({//配置接受state等选项,值为对象
  state,mutations,
  getters,actions
});

export default store;//导出store实例给main.js
// src/store/state.js

let state={
  count:0
}

export default state;
// src/store/actions

let actions = {
  jia:({commit,state},payload)=>{
   	//state 公共状态 payload 负载  payload有没有传递时,是事件对象
    commit('increment',payload)
  }
};

export default actions;
// src/store/mutations.js

let mutations={
  increment:(state,payload)=>{
    //state 仓库|公共数据 payload携带的负载 payload有没有传递时,是undefined
    state.count+=payload;//mutations 不做业务,只负责突变state
  }
};

export default mutations;
// src/store/getters.js

let getters = {
  count:(state)=>{
    //返回处理后的state  ~~ computed  
    return state.count % 2 === 0 ? state.count + '偶数': state.count + '奇数'
  }
};
export default getters;

组件中使用

<!--声明式 发送请求-->
<div @click="类型(负载)"></div>

<!--展示状态-->
<div>{{count}}</div>
<div>{{$store.state.count}}</div>
import {mapActions, mapMutations,mapState,mapGetters} from 'vuex'
import store from './plugins/vuex.js';
export default {
  name: 'app',
  methods:{
    jia(){
      //编程式 发送请求
      store | this.$store.commit('类型',数据/负载/payload) //-> mutations
			store | this.$store.dispatch('类型',数据/负载/payload)  //-> actions
			store | this.$store.dispatch|commit({type:'类型',payload:负载}) // payload={}
    }
  }, 
  
  //mapActions 用来接管methods,返回一个对象
  methods:mapActions([
    'jia'
  ])
  
  //mapMutations 接管methods,跳过actions找mutations
  methods:mapMutations([
    'increment'
  ])
  
  methods:{
    ...mapMutations([//mapMutations 返回来一个对象
      'increment'
    ]),
    ...mapActions(['jia']),//mapActions 返回来一个对象
      
    show(){//组件内部methods
      ...
    }
  },
    
  //mapGetters接管computed,返回一个对象
  computed:mapGetters([
      'count'
  ]),
  
  computed:mapState([
      'count'
  ]),
  computed:{
    ...mapState([//mapState返回一个对象
      'count'
    ]),
    ...mapGetters([
      'count'
    ])
    
    count(){ //组件内容计算属性
      return this.$store.state.count % 2 === 0 ? 
        this.$store.state.count + '偶数'	: 
      	this.$store.state.count + '奇数'
    }
  }
}

vuex融入到项目当中

创建types:收藏vuex提交类型,便于后期修改,和一些types工具检查

路由监听:找一个不会被卸载组件,做数据观测(属性检测 watch),commitmutations突变state.nav数据

拦截器:请求和响应前后,commit->mutations突变state.bLoading

token校验

main.js获取本地token

// src/plugins/router.js

//同步localStorage 到 vuex  防止强刷
let local = window.localStorage.getItem('user');

if(local){
  store.commit('USER',JSON.parse(local))
}

拦截器携带到后端校验

每次发生请求,都要携带token到后端校验是否过期,拦截器携带从vuex中抓取token ,带在headers,成功返回对应接口的数据, 失败返回未登录的信息,拦截器负责前端路由的跳转

// src/plugins/axios.js

//axios的封装
import axios from 'axios';
import Vue from 'vue';
import store from './vuex.js'
import router from './router.js'

// 添加一个请求的拦截
axios.interceptors.request.use((config) => {
	
  //抓取公共state的token
  let token = store.state.user.token;
  token = token ? token : '';
  config.headers = {
    'token': token
  }
  store.commit('LOADING', true)//控制loading显示
  return config;
  
}, function(error) {
  // 请求错误时做点事
  return Promise.reject(error);
});

//添加一个响应拦截
axios.interceptors.response.use(function(response) {
  store.commit('LOADING', false)
  
  //token过期: 返回值2,当前路由不是login时跳转 
  if (response.data.err == 2 && !router.currentRoute.fullPath.includes('/login')) {
    router.push({
      path: '/login',
      query: {
        path: router.currentRoute.fullPath// 携带当前路径,便于登录后返回,登录时需要同步vuex和localStorage
      }
    })
  }
  return response;

}, function(error) {

  return Promise.reject(error);
  
});

Vue.prototype.$axios = axios;
window.axios = axios;

export default axios;

vuex的module

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

为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter

// src/plugins/vuex.js
import goods from '../store/modules/goods.js'
import detail from '../store/modules/detail.js'

let store = new Vuex.Store({
  state,actions,mutations,getters,//根作用域
  modules:{//模块作用域
    goods,detail
  }
});
//src/store/modules/goods.js
const state={
  list:[]
};

const actions={
  A_GOODS_LIST({dispatch,commit,getters,state,rootState,rootGetters},payload){
    //state,getters 为模块内部,局部
    commit('M_GOODS_LIST',payload)//  局部化访问模块内部
    dispatch('jia',payload,{root:true})//访问根 dispatch 或 commit
    dispatch('模块名/类型',payload)//模块访问模块
  },
  GLOBAL_A_JIA:{//模块注册全局 action
    root:true,
    handler({dispatch,commit,getters,state,rootGetters,rootState},payload){
      ...
    }
  }
};
const mutations={
  M_GOODS_LIST(state,payload){
    state.list=payload;//state是局部
  }
};
const getters={
  G_GOODS_LIST(state,getters,rootState,rootGetters){
    return '处理后的'+state.list
  }
};

export default{
  namespaced:true,//命名空间的模块。模块内部直接调用,外部需要命名空间(见组件)
  state,actions,mutations,getters
}

//xx.vue
<template>
  <div id="app">
    <button @click="show">goods请求</button>
    <button @click="A_GOODS_LIST(5)">发送goods模块下的actions请求</button>
    <button @click="M_GOODS_LIST(5)">goods模块下的mutations请求</button>
    <br>
    goods模块下的getters属性: {{G_GOODS_LIST}}
    <br>
    goods模块下的state数据: {{$store.state.goods.list}}/{{list}}
    <br>
    <button @click="GLOBAL_A_JIA(3)">调用模块内部的全局actions</button>
  </div>
</template>

<script>

import { mapGetters, mapActions, mapMutations, mapState,createNamespacedHelpers } from 'vuex'
//const { mapState, mapActions } = createNamespacedHelpers('模块名')
//使用 createNamespacedHelpers 创建基于某个命名空间辅助函数
export default {
  methods: {
    ...mapActions(['jia', 'jian', 'odd', 'yibu','GLOBAL_A_JIA']),//GLOBAL_A_JIA定义在模块内的全局actions
    ...mapActions('goods', ['A_GOODS_LIST']),//调用模块,添加了goods的命名空间
    ...mapMutations(['increment']),//全局
    ...mapMutations('detail', ['M_DETAIL_LIST']),//模块内部
    show () { 
      this.$store.dispatch('goods/A_GOODS_LIST', 12)//调用模块内部
      this.$store.commit('goods/M_GOODS_LIST', 12)
      this.$store.commit('increment', 12)//调用根
    },
    
  },
  computed: {
    ...mapGetters(['getCount']),//根
    ...mapGetters('goods', ['G_GOODS_LIST','type2']),//模块
    ...mapState(['count']),//根
    ...mapState('goods',['list']),//模块
    
    //createNamespacedHelpers
    ...mapState({
      a: state => state.a, //抓取模块内部的state.a作为当前组件的a属性使用
      b: state => state.b
    })
  },

  components: {
  }
}
</script>

mutations无引用负载

actions内部要传给mutations的负载(payload),不可以是源state的引用,否则有时会出现非响应式情况,如购物车