Vuex

Vuex

一、Vuex概念

存在的问题:

  • 一个状态,很多个组件都想用

  • 多个组件共享一个状态,这个变量放哪个组件中都不合适

是什么

  • 状态管理模式
  • 简单说,需要多个组件共享的变量全部存放在一个对象中
  • 然后这个对象放在顶层的Vue实例中,其他组件可以使用
  • 如,用户的一些信息(登录状态、名称头像、位置)。商品收藏、购物车

1.1.单界面的状态管理

  • State—>View—>Actions—>State

    • State:状态,可以看成data中的属性
    • View:视图层,针对Sate的变化,显示不同的信息
    • Actions:用户的各种操作,如点击、输入等,导致状态变化
  • 单界面使用data就可以实现,不需要vuex

1.2.多界面的状态管理

  • 多个视图(View)依赖于一个状态(State),一个状态改变了,多个视图界面需要进行更新。
  • 不同界面的的Actions,都想修改同一个状态。
  • 全局单例模式化:统一管理共享的状态,可以按照规定好的规定,进行访问修改等操作

二、基本使用

安装vuex,项目在运行起来也要用,所以不能加-dev。

npm install vuex 

新建store配置文件:store/index.js,创建store实例

import Vue from 'vue'
import Vuex from 'vuex'
// 1、安装插件
Vue.use(Vuex)
//2.创建store实例对象
const store=new Vuex.Store({
    state:{
        count:1
    },
    mutations:{  
    },
    actions:{
    },
    getters:{
    },
    modules:{
    }
})
//3.将store对象导出去,挂载到Vue实例中
export default store
//3.挂载到Vue实例中
import store from './store'
new Vue({
    store,
    render: h => h(App),
}).$mount('#app')

其他组件中使用store保存的状态

this.$store.state.属性名

三、state单一状态树

  • state这些属性都会被加到响应式系统中,而响应式系统会监听属性的变化。
  • 当属性发生变化时会通知界面中用到该属性的地方,让界面刷新

3.1.修改state

方式一:直接this.$store.state.属性名=值的形式可以直接修改state中的值

  • 这个方式不好,直接修改stata,devtool工具没办法跟踪。
addClick(){
    this.$store.state.count++;
},

方式二:通过this.$store.commmit()调用mutation更新修改

  • 在devtool工具可以追踪到值的变化
//组件中通过commit调用mutation更新
addClick2(){
	this.$store.commit('add')
}
/*****store/index.js*****/
const store=new Vuex.Store({
    state:{
        counter:1,
    },
    //mutation定义
    mutations:{ 
        add(state){
            state.counter+=2
        },
    }
})

四、getters

  • getters相当于vue中的计算属性
  • 数据经过一系列变化后可以用计算属性

4.1.基本使用

/*****store/index.js*****/
const store = new Vuex.Store({
    state: {
        students: [
            { id: "110", name: "HHH", age: 18 },
            { id: "111", name: "LLL", age: 24 },
            { id: "112", name: "GGG", age: 30 },
            { id: "113", name: "III", age: 10 },
        ],
    },
    getters: {
        more20stu(state) {
            return state.students.filter((s) => s.age > 20);
        },
        // getters作为参数
        more20stuLength(state, getters) {
            return getters.more20stu.length;
        },
    },
});
<p>返回年龄大于20岁的人{{$store.getters.more20stu}}</p>
<p>年龄大于20岁的人数{{$store.getters.more20stuLength}}</p>

4.2.传递参数

  • 默认是不能传递参数
  • 如果希望传递参数,那么只能让getters本身返回另一个函数
/*****store/index.js*****/
getters: {
    moreAgeStu(state){
        //想让别人传参数的话是返回一个函数
        return age=>{
            return state.students.filter((s)=>s.age>age)
        }
    }
}
<!--需求:返回大于多少岁是别人传的-->
<p>{{$store.getters.moreAgeStu(12)}}</p>

五、mutations

5.1.状态更新

  • 异步操作去action中做,等做完异步操作后,再去mutation中修改对应的状态

  • (基本使用见上,修改state)

5.2.传递参数

//组件中通过commit调用mutation更新
//1、普通提交风格,只能传递一个参数
addCountClick(count) {
   this.$store.commit("addCount", count);
},
addCountClick1(count) {
    this.$store.commit("addCount1", {count });
},
//2、特殊的提交风格
addCountClick2(count){
    this.$store.commit({
        type:'addCount2',
        count,
    })
}
/*****store/index.js*****/
mutations:{
    //1、普通提交风格
    addCount(state, count) {
      state.count += count;
    },
    addCount1(state, payload) {
      state.count += payload.count;
    },
    //2、特殊的提交风格
    addCount2(state,payload){
         state.count += payload.count
     },
}

5.3.响应式

  • state中的属性都会被添加至响应系统,可以直接及修改
  • 没有在state中属性,添加
    • Vue.set,响应式添加,界面会发生变化
    • 用新对象给旧对象复值。新对象=Object.assign({},旧对象,新增对象);
    • .属性[属性],界面不会发生改变
  • 删除属性,该方式做不到响应式
    • Vue.delete,响应式删除
    • delete做不到响应式,界面不会发生改变
  • 注:发现一个小问题:.和delete,如果当前操作或操作后有个响应式一起,界面也会改变
/*****store/index.js*****/
const store = new Vuex.Store({
    state: {
        info:{
            name:'aaa',
            age:18,    
            height:188
        }
    },
    mutations: {
        updateInfo(state){
            //1、state中的属性都会被添加至响应系统,可以直接及修改
            state.info.name='hhh';  
            //2、没有在state中属性,添加
            //2.1.Vue.set响应式添加,界面会发生变化
            Vue.set(state.info,'address','福州');
            //2.2.用新对象给旧对象复值
            state.info=Object.assign({},state.info,{address:'福州'})
            //2.3.'.属性'或'[属性]',界面不会发生改变
            // state.info['address']='福州';
            //3、删除属性,该方式做不到响应式
            //3.1.Vue.delete响应式删除
            Vue.delete(state.info,'age')
            // delete做不到响应式,,界面不会发生改变
            delete state.info.age

            console.log(state.info);
        }
    },
});

5.4.同步函数

  • mutation中的方法必须是同步的的
  • 在mutation中进行异步操作,界面变化了,但是devtool值没有变没记录下来,变成记录是错误的信息
mutations:{
    updateInfo(state){
        //错误代码:不能在这里进行异步操作
        setTimeout(()=>{
            state.info.name='hhh'
        },1000)
    }
}

5.5.### 常量类型

新建常量mutation-types.js文件,定义常量

export const INCREMENT='increment'

store/index.js使用这个常量

import {INCREMENT} from "./mutation-types";
mutations: {
    [INCREMENT](state){
        state.count++
    },
}

组件中使用commit调用这个常量类型函数

import {INCREMENT} from './store/mutation-types'
addClick3() {
	this.$store.commit(INCREMENT);
},

5.6.devtools

  • 通过mutation更新stata状态,devtool工具可以进行跟踪。
  • actions是用来放异步请求的,异步直接放在mutation里面,devtool没法记录哪里来的。
  • 异步操作去action中做,等做完异步操作后,在去mutation中修改对应的状态
  • devtool在使用vuex时,多页面中都来修改stata,希望可以进行跟踪,当某个界面修改错的时候,就可以跟踪到时哪里修改错了,之后进行调试更加的方便

六、actions

  • 异步请求需要放在actions中

通过this.$store.dispatch调用actions更新状态

//写法1:
updateInfo1() {
    // this.$store.dispatch("aUpdateInfo", "我是payload");
    this.$store.dispatch("aUpdateInfo1", {
        message: "我是携带的信息",
        success: () => {
            console.log("里面已经完成了");
        }
    });
},
//写法2:promise的写法
updateInfo2() {
    this.$store.dispatch("aUpdateInfo2", "我是携带的信息").then((res) => {
        console.log("里面完成了提交");
        console.log(res);
    })
},    

异步修改信息

/*****store/index.js*****/
const store=new Vuex.Store({
    state:{
        info:{
            name:'aaa', 
            age:18,    
            height:188 
        }
    },
    mutations:{
        updateInfo(state){
            state.info.name='hhh';
        }
    },
    actions:{  
        //写法1:
        aUpdateInfo1(context,payload){
          //1.context:上下文  这里先理解成store。
          //context.rootState:根节点状态
          console.log(context.state===context.rootState)//true,这个是相等的
          setTimeout(() => {
               
            //2.actions中值修改
            //2.1.不推荐直接这样修改,,devtool中追踪不到变化
            // context.state.info.name = "LLL"; 
            //2.2.要用mutations修改
            context.commit('updateInfo');
            console.log(payload.message);
            payload.success();
          }, 1000);
        },
         //写法2:promise的写法
        aUpdateInfo2(context,payload){
            return new Promise((resolve,reject)=>{
                setTimeout(()=>{
                    context.commit('updateInfo');
                    resolve('请求返回的数据')
                },1000)
            })
        }
    }
})

七、modules

定义模块a

/*****store/index.js*****/
const moduleA={
  state:{
    name:'张三'
  },
  mutations:{
    updateName(state,payload){
      state.name=payload
    }
  },
  getters:{
    fullName(state){
      return state.name+'111'
    },
    fullName2(state,getters){
      return getters.fullName+'222'
    },
    fullName3(state,getters,rootState){
      //rootState:根节点状态
      return getters.fullName2+rootState.message
    }
  },
  actions:{
    //context:上下文,这里是a模块
    aUpdateName(context){ 
      setTimeout(()=>{
        context.commit('updateName','a王五');
      },1000)
    }
  }
}
const store=new Vuex.Store({
  state:{
     message:'我是根状态中的属性',  
  }
  modules:{
    // 抽离,可以继续定义模块
    a:moduleA
  }
})
<h2>------App内容:module中的内容------</h2>
<h2>{{ $store.state.a.name }}</h2>
<button @click="updateName">修改名字</button>
<h2>fullName:{{ $store.getters.fullName }}</h2>
<h2>fullName1:{{ $store.getters.fullName1 }}</h2>
<h2>fullName1:{{ $store.getters.fullName2 }}</h2>
<h2>fullName1:{{ $store.getters.fullName3 }}</h2>
<button @click="asyncUpdateName">异步修改名字</button>
<script>
export default {
  methods:{
    updateName(){
       this.$store.commit('updateName','HHH')
    },
    asyncUpdateName(){
       this.$store.dispatch('aUpdateName');
    }
  }
}
</script>

八、项目结构

  • 可以都单独抽离出来一个文件夹
import mutations from './mutations'
import actions from './actions'
import getters from './getters'
import moduleA from './modules/moduleA'
const state={}
const store=new Vuex.Store({
  state,
  mutations,
  actions,
  getters,
  modules:{
    // 抽离,可以继续定义模块
    a:moduleA
  }
})
上一篇:vuex中 this.$store.dispatch() 与 this.$store.commit()方法的区别


下一篇:vue的组件之间传递数据vue-bus和vuex