详解vuex 解决多个组件之间的通信问题

详解vuex

1.安装 yarn add vuex

2.引入vuex 并且让vue使用vuex

新建文件
src/store/index.js(仓库

//引入vuex
import Vuex from "vuex"
import Vue from "vue"
Vue.use(Vuex)  //让vue使用Vuex
//创建store仓库
let store = new Vuex.Store({//里面写store相关的配置项
})
export default store;  //需要暴露出去

vuex流程图

3.在main.js入口文件里注册store

//引入vuex的实例
import store from "./store"
new Vue({
  render: h => h(App),
  router,  //就是为了让每一个组件都可以访问到路由相关的api ($router/$route)
  store    //目的就是为了让每一个组件都可以访问到vuex相关的api ($store)
}).$mount('#app')

4.在仓库中index.js 定义 以及更改状态

定义isTababrShow 以及 更改isTabbarShow状态的方法

//创建store
let store = new Vuex.Store({
    state:{ //就是用来定义多组件之间的共享状态
        isTabbarShow:true
    },
    mutations:{  //如vuex流程图,只要mutations有权限更改state,
    //同时这里也提供了很多更改状态的方法
        show(state){
            state.isTabbarShow = true
        },
        hide(state){
            state.isTabbarShow = false
        }
    } 
})

5.在主入口文件App.vue里面对状态进行关联

 <Tabbar v-show="$store.state.isTabbarShow"></Tabbar>  

6.在Search.vue触发钩子函数

一进入search页面不让tabbar显示

created(){
    //一进入search,需要更改仓库isTabbarShow将其变成false
    this.$store.commit("hide")
  },
  beforeDestroy(){
    //一旦离开search,isTabbarShow true
    this.$store.commit("show")
  }

在这里插入图片描述

在这里插入图 片描述
同步操作闭环形成

通过vuex异步请求数据

在这里插入图片描述

Cinema.vue

toSearch(){
	this.$router.push("/search")
}

发现Search与Cinema组件中的数据都是一致的,不需要进入到每个组件都请求一次数据,可以在vuex中的action里面进行异步请求即可。

1.在仓库中定义以及更改状态

store/index.js

state:{ //就是用来定义多组件之间的共享状态  this.$store.state.isTabbarShow
        isTabbarShow:true,
        cinemaList:[]
},

 actions:{
        getCinemaListAction(context){
            instance.get("/gateway?cityId=310100&ticketFlag=1&k=2654330",{
                headers:{
                  'X-Host': 'mall.film-ticket.cinema.list'
                }
              }).then(res=>{
              	// 如何更改vuex中的cinemaList这个状态? 
                // 需要触发mutations的setCinemaList这个方法 
                // 第一个参数就是触发的具体的mutations的名称  第二个参数就是给其传入的异步请求获取到的数据
                context.commit("setCinemaList",res.data.data.cinemas)
            })
        }
    },
  mutations:{ //后续唯一可以更改state的地方
        show(state){  //组件中如何调用此方法?  this.$store.commit("方法名")
            state.isTabbarShow = true
        },
        hide(state){
            state.isTabbarShow = false
        },
        setCinemaList(state,cinemaList){ 
        //第二个参数可以接受外面传递过来的参数
            state.cinemaList = cinemaList
        }
    }
})

2.在Cinema组件中进行触发action的方法:

created(){
    //需要调用dispatch触发vuex的action方法
    this.$store.dispatch("getCinemaListAction")
  }

需要在Cinema中使用vuex的状态cinemaList

  cinemaArea(){
  let newArr = this.$store.state.cinemaList.map(item=>{
    return item.districtName
  })
  return ["全城",...new Set(newArr)]
},
filterCinemaArea(){
  if(this.currentArea === "全城") return this.$store.state.cinemaList;
  return this.$store.state.cinemaList.filter(item=>{
    return item.districtName === this.currentArea ? true : false
  })
}

回到Search页面,发现vuex的全局状态管理的优势体现出来了。多组件数据共享

3.vuex存储是内存,一刷新就没有了,

如果不刷新界面,可以实现多组件的数据共享。因为vuex中的数据存内存中的,一刷新就会恢复初始值。
Search.vue

created(){
    if(this.$store.state.cinemaList.length>0){
	   console.log("vuex中的cinemaList数据有了...")
    }else{ //刷新了 vuex中没有数据
      this.$store.dispatch("getCinemaListAction")
    }
    this.$store.commit("hide")
  },

vuex的getters获取5条影院数据

开始是在Search.vue里

getCinemaListFive(){ //组件的computed依赖于data数据
	return this.$store.state.cinemaList.slice(0,5)
},

如果在别的地方,也需要用到vuex的5条数据的话,也得需要写一次上述的计算属性。

需要在store/index.js里面的getters去进行单独的定义了。

getters:{  //类似于computed计算属性 只不过里面的数据依赖于vuex的状态
        getCinemaListFive(state){
            return state.cinemaList.slice(0,5)
        },
 },

之后在Search.vue里去调用

<li
    v-for="data in $store.getters.getCinemaListFive"
    :key="data.cinemaId"
>{{data.name}}</li>

vuex辅助函数

(mapState,mapGetters,mapActions,mapMutations)

mapState

mapState这个辅助函数就是为了方便获取vuex中的状态,本来获取状态 this.$store.state.xxx

import { mapState } from 'vuex'

computed:{
    ...mapState(["cinemaList"]),  
    cinemaArea(){
      //获取vuex的状态,直接通过this既可以访问到了
      let newArr = this.cinemaList.map(item=>{
        return item.districtName
      })
     
  return ["全城",...new Set(newArr)]
},
filterCinemaArea(){
  if(this.currentArea === "全城") return this.cinemaList
  return this.cinemaList.filter(item=>{
    return item.districtName === this.currentArea ? true : false
  })
}
}

后续如果你的computed中有跟vuex的同名的,需要通过这种方式设置

...mapState({
     aaa:state=>state.cinemaList
 }),

mapGetters

这个辅助函数与mapState的用法一模一样

import {mapState,mapGetters} from "vuex"
computed:{
    ...mapState(["cinemaList"]),
    ...mapGetters(["getCinemaListFive"]),
}
 v-for="data in getCinemaListFive"    //$store.getters.getCinemaListFive

modules

因为store是单一的数据源,项目中创建的store的实例只能是一个,所以数据全都放在这一个大的实例对象中,很难去维护,所以需要对于store进行模块划分。

store/module/tabbar.js

const tabbar = {
    namespaced: true,
    state:{
        isTabbarShow:true,
    },
    mutations:{
        show(state){  //组件中如何调用此方法?  this.$store.commit("方法名")
            state.isTabbarShow = true
        },
        hide(state){
            state.isTabbarShow = false
        }
    }
}

export default tabbar;

store/module/cinema.js

import {instance} from "@/utils/http"
const cinema = {
    namespaced: true,
    state:{
        cinemaList:[]
    },
    getters:{
        getCinemaListFive(state){
            return state.cinemaList.slice(0,5)
        },
    },
    actions:{
        getCinemaListAction(context){
            instance.get("/gateway?cityId=310100&ticketFlag=1&k=2654330",{
                headers:{
                  'X-Host': 'mall.film-ticket.cinema.list'
                }
              }).then(res=>{
                // 需要触发mutations的setCinemaList这个方法 
                // 第一个参数就是触发的具体的mutations的名称  第二个参数就是给其传入的异步请求获取到的数据
                context.commit("setCinemaList",res.data.data.cinemas)
            })
        }
    },
    mutations:{
        setCinemaList(state,cinemaList){
            state.cinemaList = cinemaList
        }
    }
}

export default cinema;

store/index.js

import tabbar from "./module/tabbar"
import cinema from "./module/cinema"

//创建store
let store = new Vuex.Store({
    modules: { //通过modules将所有的分支的模块进行合并
        tabbar,
        cinema
    }
})

Cinema.vue

...mapState("cinema",["cinemaList"]),    //前提一定需要在module模块里面声明namespace:true

派发方法,也得需要按照指定的模块名字请求action与mutations

this.$store.dispatch("cinema/getCinemaListAction")

Search.vue

...mapState("cinema",["cinemaList"]),
...mapGetters("cinema",["getCinemaListFive"]),
created(){
    if(this.cinemaList.length>0){
    }else{ 
      //dispatch("module名字/action方法")
      this.$store.dispatch("cinema/getCinemaListAction")
    }
    
    //commit("module名字/commit方法")
    this.$store.commit("tabbar/hide")
  },
  beforeDestroy(){
    //commit("module名字/commit方法")
    this.$store.commit("tabbar/show")
  }

App.vue

import {mapState} from "vuex"

computed:mapState("tabbar",["isTabbarShow"])
<Tabbar v-show="isTabbarShow"></Tabbar>  

Search

<button @click="back">取消</button>
methods:{
    back(){
      this.$router.back()
    }
},
mapActions

这个辅助函数用法与上面一致,但是需要定义在methods里面

import { mapState,mapActions } from 'vuex'
methods:{
    ...mapActions("cinema",["getCinemaListAction"]),
}
created(){
    if(this.cinemaList.length === 0){
      // this.$store.dispatch("cinema/getCinemaListAction")
      this.getCinemaListAction()
    }
  }
mapMutations

这个辅助函数用法与上面一致的用法

import {mapState,mapGetters,mapMutations,mapActions} from "vuex"
methods:{
    ...mapActions("cinema",["getCinemaListAction"]),
    ...mapMutations("tabbar",["show","hide"]),
}
created(){
    if(this.cinemaList.length>0){

    }else{ //刷新了 vuex中没有数据
      this.getCinemaListAction()
    }
    this.hide()
},
beforeDestroy(){
    this.show()
}

vuex 项目结构

Vuex 并不限制你的代码结构。但是,它规定了一些需要遵守的规则:

  1. 应用层级的状态应该集中到单个 store 对象中。
  2. 提交 mutation 是更改状态的唯一方法,并且这个过程是同步的。
  3. 异步逻辑都应该封装到 action 里面。

只要你遵守以上规则,如何组织代码随你便。如果你的 store 文件太大,只需将 action、mutation 和 getter 分割到单独的文件。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值