Vuex的基本使用

853 阅读3分钟

1. 为什么使用Vuex?

在Vue的组件化开发中,父组件和子组件之间可以相互传递数据,但是依然有两个问题:

  • 如果想在子组件中获取祖先组件(父组件以上的组件)中的数据,必须一层一层的进行传递
  • 如果两个兄弟组件想传递数据,则必须先将数据传递给父组件,借助父组件才能完成兄弟组件传递数据;如果两个组件不在同一个父组件内,则也需要传递到祖先组件。 通过以上两个问题可以看出,不使用Vuex的情况下,兄弟组件之间传递、共享数据有多麻烦。

使用Vuex,可以将组件中公共的部分抽取出来,独立于各个组件,程序中的任何组件都可以获取和修改Vuex保存的公共数据。

2. 如何使用Vuex?

四个步骤:引入Vuex、创建Vuex对象、保存store、使用Vuex

2.1 引入Vuex

在官网下载Vuex:vuex.vuejs.org/zh/installa…

<script src="./lib/vue.js"></script>
<!-- 1. 导入Vuex -->
<!-- 注意点:在导入Vuex之前必须先导入Vue -->
<script src="./lib/vuex-3.6.2.js"></script>

2.2 创建Vuex对象

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)
// 如果已经通过script引入了vuex文件可以忽略以上代码

const store = new Vuex.Store({
  state: {
    count: 0
  },
  // 注意点:在执行mutations中定义的方法的时候,系统会自动给这些方法传递一个state参数
  // state中就保存了共享的数据
  mutations: {
    increment (state) {
      state.count++
    }
  }
})
  • state:相对于组件中的data,专门用于保存共享数据
// 在组件中可以通过"this.$store.state.变量名"来获取vuex中的数据,this.$store.state是固定字段
console.log(this.$store.state.count); // 0
  • mutations: 定义在vuex内部的,用于对vuex保存数据进行操作的函数,在vuex中不推荐由组件直接修改vuex中的数据,通过调用在vuex内部定义的函数更有利于维护代码。
// 在组件中可以通过"this.$store.commit("函数名")"来调用vuex中的函数
add() {
    this.$store.commit("increment");
}

2.3 添加store的key

在祖先组件(最外层的组件)中添加stroe,在祖先组件中定义的组件就都可以使用vuex

 const store = new Vuex.Store({
    // 这里的state就相对于组件中的data,就是专门用于保存共享数据的
    state: {
        msg: "深海鱼"
    },
    /*  mutations: {
         increment(state) {
             state.count++
         }
     } */
});
Vue.component("grandfather", {
    template: "#grandfather",
    // 3. 在祖先组件中添加store的key保存Vuex对象
    // 只要祖先组件中保存了Vuex对象,那么祖先组件和所有的后代组件就可以使用Vuex中保存的共享数据
    store,
    // 父组件
    components: {
        "father": {
            template: "#father",
            components: {
                "son1": {
                    template: "#son1",
                }
            }
        }
    }
})

2.4 使用Vuex

可以直接使用规定的字段来获取Vuex中的数据

<template id="grandfather">
    <div>
        <h1>{{this.$store.state.msg}}</h1>
        <father></father>
    </div>
</template>
<!-- 父组件使用vuex -->
<template id="father">
    <div>
        <!-- 4. 在使用Vuex中保存的共享数据的时候,必须通过如下的格式来使用 -->
        <h2>{{this.$store.state.msg}}</h2>
        <son1></son1>
    </div>
</template>
<!-- 子组件使用vuex -->
<template id="son1">
    <div>
        <h3>{{this.$store.state.msg}}</h3>
    </div>
</template>

3. getters属性

Vuex的getters相对于组件中的计算属性

 const store = new Vuex.Store({
    // state:用于保存共享数据
    state: {
        msg: "深海鱼"
    },
    // mutations: 用于保存修改共享数据的方法
    mutations: {

    },
    getters: {
        format(state) {
            console.log("getters方法被执行了");
            return state.msg + '没了没了';
        }
    }
});
<template id="father">
   <div>
        {{this.$store.state.msg}} 
        {{this.$store.getters.format}} 
        {{this.$store.getters.format}} 
        {{this.$store.getters.format}}
   </div>
</template>

输出结果:

image.png

暂时写到这里,Vuex之后的部分会随着学习不断更新。

4. 应用

在真实的应用开发中,可能会有很多条数据或状态存储在vuex中,如果我们把每条数据都存储在state中,就必须对每条数据建立mutations方法,就必须对每个方法建立actions触发事件,也必须在getters中定义返回state当中值的事件。

我们可以将state、mutations、actions、getters单独建立文件,在文件中利用export default将其暴露,再在index.js中逐一导入,就可以实现很方便的管理,接下来是实例:

音乐在线项目

在这个项目中,vuex被用来管理播放页、迷你播放组件,以及迷你组件当中播放列表页面的显示和隐藏状态,因为这些页面和组件可以在项目的任意位置被使用,所以最适合用vuex来进行全局管理。

vuex中定义

index.js

import Vue from 'vue'
import Vuex from 'vuex'
import state from './state'
import mutations from './mutations'
import actions from './actions'
import getters from './getters'

Vue.use(Vuex)

export default new Vuex.Store({
  // state: 用于保存全局共享的数据
  state,
  // mutations:用于保存修改全局共享的数据的方法
  mutations,
  // actions:用于保存触发mutations中保存的方法的方法
  actions,
  modules: {
  },
  getters
})

state.js

export default {
  isFullScreen: false,
  isShowMiniPlayer: false
}

mutations.js

  • 这里可以看到多引入了一个mutations-type,原因是如果使用直接使用字符串,填写错误时不会报错,如果定义了一个公共变量,变量名错误时就会报错。
import { SET_FULL_SCREEN, SET_MINI_PLAYER } from './mutations-type'

export default {
  /* eslint-disable */

  /* changeFullScreen (state, flag) {
    state.isFullScreen = flag
  } */
  [SET_FULL_SCREEN] (state, flag) {
    state.isFullScreen = flag
  },
  [SET_MINI_PLAYER] (state, flag) {
    state.isShowMiniPlayer = flag
  }
}

mutations-type

export const SET_FULL_SCREEN = 'SET_FULL_SCREEN'
export const SET_MINI_PLAYER = 'SET_MINI_PLAYER'

actions

import { SET_FULL_SCREEN, SET_MINI_PLAYER } from './mutations-type'
export default {
  setFullScreen ({ commit }, flag) {
    commit(SET_FULL_SCREEN, flag)
  },
  setMiniPlayer ({ commit }, flag) {
    commit(SET_MINI_PLAYER, flag)
  }
}

getters

export default {
  isFullScreen (state) {
    return state.isFullScreen
  },
  isShowMiniPlayer (state) {
    return state.isShowMiniPlayer
  }
}
播放页面

*css部分代码不做展示

<template>
  <transition  v-on:enter="enter"   @leave="leave">
        <!-- 在这里使用了vuex中定义的数据,在getters中定义了获取方法后在需要使用的组件的computed中定义...mapGetters(['getters中方法名']),就可以通过this.方法名的方式获取到所需数据 -->
    <div class="normal-player" v-show="this.isFullScreen">
    <div class="player-waepper">
      <PlayHeader></PlayHeader>
      <PlayerMiddle></PlayerMiddle>
      <PlayerBottom></PlayerBottom>
    </div>
    <div class="player-bg">
        <img src="https://dfzximg01.dftoutiao.com/news/20210614/20210614134446_5350398987762e5ff84917e9f52af0c9_4.png" alt="">
    </div>
  </div>
  </transition>
</template>

<script>
import PlayHeader from './PlayHeader'
import PlayerMiddle from './PlayerMiddle'
import PlayerBottom from './PlayerBottom'
import { mapGetters } from 'vuex' // vuex在这里被引入
import Velocity from 'velocity-animate'
import 'velocity-animate/velocity.ui'

export default {
  name: 'NormalPlayer',
  components: {
    PlayHeader,
    PlayerMiddle,
    PlayerBottom
  },
  computed: {
    ...mapGetters([
      'isFullScreen' // 因为没有调用vuex当中的方法,仅是获取属性值,只需要在computed中定义...mapGetters({})
    ])
  },
  methods: {
    enter (el, done) {
      Velocity(el, 'transition.shrinkIn', { duration: 500 }, () => {
        done()
      })
    },
    leave (el, done) {
      Velocity(el, 'transition.shrinkOut', { duration: 500 }, () => {
        done()
      })
    }
  }
}
</script>
歌单页面
<template>
  <ul class="detail-bottom">
      <li class="bottom-top">
          <div class="bottom-icon"></div>
          <div class="bottom-title">播放全部</div>
      </li>
       <!-- 歌单中的歌曲被点击时更改vuex中管理的数据状态,使歌曲播放页显示 -->
      <li v-for="value in playlist" :key="value.id" class="item" @click="selectMusic">
          <h3>{{value.name}}</h3>
          <p>{{value.al.name}} - {{value.ar[0].name}}</p>
      </li>
  </ul>
</template>

<script>
// 因为需要更改数据状态,所以从vuex需要引入mapActions
import { mapActions } from 'vuex'
export default {
  name: 'DetailBottom',
  props: {
    playlist: {
      type: Array,
      default: () => [],
      required: true
    }
  },
  methods: {
  // 这里同样只需要在其中写明需要调用的actions方法,用数组+字符串的形式,就可以在外部通过this.方法名调用
    ...mapActions([
      'setFullScreen', // 将`this.setFullScreen(true)`映射为 `this.$store.dispatch('setFullScreen', true)`
      'setMiniPlayer'
    ]),
    selectMusic () {
    //   this.$store.dispatch('setFullScreen', true)
    // 可以看到actions方法可以传递参数
      this.setFullScreen(true)
      this.setMiniPlayer(false)
    }
  }
}
</script>
迷你播放器组件

迷你播放器组件既需要获取自身是否显示的状态,又需要监听点击事件来控制大播放页面是否显示,这时就需要同时从vuex中引入mapActions和mapGetters。

<template>
<transition @enter="enter" @leave="leave">
   <div class="mini-player" v-show="this.isShowMiniPlayer">
      <div class="player-warpper">
          <div class="player-left" @click="showNormal">
              <img src="https://p1.music.126.net/8y8KJC1eCSO_vUKf2MyZwA==/109951165796899183.jpg" alt="" >
              <div class="player-title">
                  <h3>演员</h3>
                  <p>薛之谦</p>
              </div>
          </div>
          <div class="player-right">
            <div class="play"></div>
            <div class="list" @click.stop="showList"></div>
          </div>
      </div>
  </div>
</transition>

</template>

<script>
import { mapActions, mapGetters } from 'vuex'
import Velocity from 'velocity-animate'
import 'velocity-animate/velocity.ui'

export default {
  name: 'MiniPlayer',
  methods: {
    ...mapActions([
      'setFullScreen',
      'setMiniPlayer'
    ]),
    showNormal () {
      this.setMiniPlayer(false)
      this.setFullScreen(true)
    },
    showList () {
      this.$emit('showList')
    },
    enter (el, done) {
      Velocity(el, 'transition.bounceUpIn', { duration: 500 }, () => {
        done()
      })
    },
    leave (el, done) {
      Velocity(el, 'transition.bounceDownOut', { duration: 500 }, () => {
        done()
      })
    }
  },
  computed: {
    ...mapGetters([
      'isShowMiniPlayer'
    ])
  }
}
</script>

记忆点:

  • mapActions 引入后在methods中定义...mapActions([])
  • mapGetters 引入后在computed中定义...mapGetters([])