详解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; //需要暴露出去
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 并不限制你的代码结构。但是,它规定了一些需要遵守的规则:
- 应用层级的状态应该集中到单个 store 对象中。
- 提交 mutation 是更改状态的唯一方法,并且这个过程是同步的。
- 异步逻辑都应该封装到 action 里面。
只要你遵守以上规则,如何组织代码随你便。如果你的 store 文件太大,只需将 action、mutation 和 getter 分割到单独的文件。