vuex简单使用

362 阅读5分钟

什么是Vuex?

  • 1.Vuex首先是一个vue插件,Vue中使用插件通过Vue.use()方法

  • 2.一个应用我们可能会在不同的页面或不同的组件里面使用(改变)一些公共数据时,可以考虑使用Vuex。

  • 3.有哪些常用公共信息(例:登陆信息,菜单信息,主题,某个复杂模块的数据)

属性名词解释

{
  state:{}        //存放的公共数据
  mutations:{},   //同步改变数据的方法(能直接操作state的唯一途径)
  actions:{},     //异步方法(例:异步请求数据成功后,再调用mutations去改变state)
  getters:{},     //类似vue的计算属性,过滤或直接返回state里面的值
  modules:{}      //模块化,例:订单模块的公共数据,商品模块的公共数据
}

基础使用

前提注意:Tip.
1.数据是存在vuex里面的,所以取数据只能从vuex取,改变数据也只能通过vuex提供的方法改变

1.新建src/store/index.js文件夹(该文件夹负责vuex的功能)
import Vue from 'Vue';
import Vuex from 'vuex'
Vue.use(Vuex)

const store = new Vuex.Store({
  state:{  //公共数据全存这里
    number:1,
    name:'doudou'
  },
  getters:{
     //直接返回值
     numGetter(state){//返回双倍:number * 2
       return state.number * 2
     },
     //返回一个方法
     numGetterByMul(state){
      return (mul)=>{
         return state.number * mul
      }
     }
  },
  mutations:{
    changeMutations(state,payload){
      console.log('state---->',state) //就是上面的state对象
      console.log('payload-->',payload) //commit方法传过来的参数{number:20},看下面
      state.number = payload.number
    },
    changeNameMutations(state,payload){
      state.name = payload.name
    }
  },
  actions:{
    actionA(context){
      //其他逻辑操作
    }
  
    async changeNameActions(context,payload){
      // 这里还可以等待actionA完成呢(这里假装一下)
      // await actionA()
    
      //就是整个vuex的实例
      console.log('context---->',context) 
      //this.$store.dispatch方法传过来的参数{number:20}
      console.log('payload-->',payload) 
      
      //通过commit方法调用mutations方法,并把参数传递给mutations里的方法,请求可以放这里面写
      setTimeout(()=>{
         context.commit('changeNameMutations',payload)
      },3000)
    },
  }
})

export default store //暴露文件

2.main.js里面挂载在根结点上,让所有应用的组件都能直接使用vuex(此时可以在任何组件里使用this.$store,这个this.$store就代表vuex)
import store from './store/index.js'
new Vue({
  store:store,  //这里代表vuex,即this.$store
  render:(h)=>h(App)
}).$mount('#app')

3.PageA.vue页面使用Vuex数据:(使用通过this.$store.state获取数据)
<template>
  <div>
    直接使用state:{{$store.state.number}}
    直接使用getters:{{$store.getters.numGetter}}
    直接使用getters:{{$store.getters.numGetterByMul(5)}}
  </div>
</template>
<script>
export default{
  create(){
    console.log(this.$store.state)  //打印
    console.log(this.$store.getters.numGetter)  //打印
    console.log(this.$store.getters.numGetterByMul(5))  //获取5倍
  }
}
</script>


4.PageB.vue页面改变Vuex数据:(直接改变数据通过this.$store.commit('方法名字','传的参数')方法改变)
<template>
  <button @click="changeMutations">mutations改变数据</button>
  <button @click="changeActions">actions改变数据</button>
</template>
<script>
export default{
  methods:{
    changeMutations(){
      //通过vuex提供的commit方法调用自己在上面写的changeMutations方法
      
      //写法1(推荐)
      this.$store.commit('changeMutations',{number:20})
      
      //写法2(不推荐)
      this.$store.commit({
        type:'changeMutations', //方法名字
        number:20
      })
    },
    changeActions(){
       this.$store.dispatch('changeNameActions',{name:'❤️的名字'})
    }
  }
}
</script>

总结:
1.通过this.$store.state 获取数据
2.通过this.$store.commit(mutations方法名字,传递参数)调用mutations方法,直接改变数据。
3.getters类似计算属性computed,且通过this.$store.getters获取
4.通过this.$store.dispatch(actions方法名字,传递参数)调用actions方法,actions方法再通过commit方法去调用mutations方法达到改变state的目的
5.actions对象里面人为规定使用异步方法,mutations对象里人为规定使用同步,人为规定只能使用mutations改变state

Vuex模块化?

当在大型项目中,每一个人分配几个模块例:张三负责订单,李四负责用户,此时订单有订单的公共信息,存在订单的模块里面,用户有用户的公共信息,存在用户的模块里面。这样就方便区分,把公共数据按作用和功能区分,拆分成小的模块,就是Vuex的模块化。每个小的模块和上面的结构一样,都有state,getters,mutations,actions等属性

1.新增src/store/modules/module-order.js(此文件代表订单模块的公共数据)
const orderModule ={
   state:{
     name:"order-name",
     number:10
   },
   getters:{
     double(state){
       return state.number * 2
     }
   },
   mutations:{
     changeName(state){
       console.log('执行orderModule模块--->changeName')
     }
   }
}
export default orderModule

2.新增src/store/modules/module-user.js(此文件代表用户模块的公共数据)
const userModule={
  state:{
    name:"user-name",
    number:10
  },
  getters:{
     double(state){
       return state.number * 2
     }
  },
  mutations:{
    changeName(state){
      console.log('执行userModule模块--->changeName')
    }
  },
  actions:{}
}
3.在src/store/index.js里面引入这两个模块,并改造代码为
import moduleOrder from './module/module-order.js'
import moduleUser from './module/module-user.js'
const store = new Vuex.Store({
  //进行模块注册
  modules:{
    'aaa':moduleOrder,   //注意此时的aaa,bbb代表模块的别名,后面操作模块均是使用它
    'bbb':moduleUser
  }
})

4.此时在PageA.vue使用数据页面时,改为以下:
<template>
  //注意此时,是通过$store.别名.state获取
  <div>
    <div>模块A的state.name:{{$store.aaa.state.name}}</div>
    <div>模块A的getters.double:{{$store.aaa.getters.double}}</div>
    <div>模块B的state.name:{{$store.bbb.state.name}}</div>
    <div>模块B的getters.double:{{$store.bbb.getters.double}}</div>
  </div>
</template>

5.此时在PageB.vue改变数据页面调用this.$store.commit('changeName')时,moduleOrder和moduleUser里面的changeName都会触发(因为虽然两个模块都注册在/src/store/index.js 最外面的store的modules里面,但是此时并没有实现命名空间namespaced,此时,每个模块的mutations,actions都属于顶层上,此时需要加上namespaced进行彼此之间的隔离)
<template>
  <button @click="changeModuleOrder">改变订单模块name</button>
  <button @click="changeModuleUser">改变用户模块name</button>
</template>
<script>
export default{
  methods:{
    changeModuleOrder(){
      this.$store.commit('changeName')
    },
    changeModuleUser(){
      this.$store.commit('changeName')
    },
  }
}
</script>

6.两个模块的changeName都触发不是我们想要的效果,应该是调用各自模块的mutations只触发对应的,此时需要分别在各个模块加上namespaced:true属性,即:
/src/store/modules/module-order.js
const orderModule={
  namespaced:true, //订单模块加上这句,注意就是加上这句代码
  state:{},
  getters:{},
  actions:{}
}

/src/store/modules/module-user.js
const orderModule={
  namespaced:true, //用户模块加上,注意就是加上这句代码
  state:{},
  getters:{},
  actions:{}
}
此时,两个模块就进行了彼此之间的隔离。

7.对应PageB.vue改变模块数据的方法调整为,此时就能正常触发对应模块的。
<template>
  <button @click="changeModuleOrder">改变订单模块name</button>
  <button @click="changeModuleUser">改变用户模块name</button>
</template>
<script>
export default{
  methods:{
    changeModuleOrder(){
      //this.$store.commit('changeName') 改变为以下方式,aaa为上面模块注册时的别名
      this.$store.commit('aaa/changeName')
    },
    changeModuleUser(){
      //this.$store.commit('changeName') 改变为以下方式,bbb为模块注册时的别名
      this.$store.commit('bbb/changeName')
    },
  }
}
</script>

总结:
1.每个模块和基本写法一样,里面都有state,getters,mutations,actions属性
2.每个模块需要在根模块(/src/store/index.js)里面注册在modules里面
  const store = new Vuex.Store({
    modules:{
      'aaa':moduleOrder,  //注意别名aaa,bbb 
      'bbb':moduleUser
    }
  })
3.获取state更改为this.$store.别名.state  (例:this.$store.aaa.state)
4.获取getters更改为this.$store.别名.getters
5.当给模块加上namespaced:true属性进行模块隔离时,调用各自对应模块更改为:this.$store.commit('模块别名/MuTations方法名') 或 this.$store.dispatch('模块别名/Actions方法名')

6.当没有给模块加上namespaced:true属性时,若触发 this.$store.commit('changeName')方法时,只要有changeName方法的模块,都会被触发。

7.当模块化时,必须强制性给每个模块都加上namespaced:true属性

为什么使用Vuex而不是Window对象,LocalStorage?

标题VuexWindow对象LocalStorage
响应式非响应式非响应式
全局污染直接挂载window对象,多个会冲突,和污染全局能直接被发现
流程控制可流程控制(具体实现下面)多个异步操作混乱,不知道具体哪个时间段,是哪个异步改变的state同window对象一样
插件有vue-dev-tools配合调试

为什么Vuex不能在Mutations里面写异步?

  • 1.当在mutations里面使用异步时,虽然程序不报错,但是若异步改变state时,我们无法确定state是何时改变的(异步时长有不确定性)
  • 2.所以我们人为规定,mutations只能使用同步,在actions里使用异步
  • 3.通过在actions里面去调用mutations去达到改变state的目的
  • 4.此时我们只需要关注mutations,因为它是同步的,所以就知道何时改变的state