一、 Vuex 是什么?

Vuex 是一个专门为 vue.js 应用程序开发的状态(状态就是数据)管理模式,它采用集中式存储管理应用的状态。相当于把组件中的数据提升到一个全局的地方,这个地方就是 Vuex 的 store(仓库),由 Vuex 统一管理,如果某个组件需要这个数据,直接从 store 中获取。

如果要修改存在 Vuex 中的数据,需要在定义 store 时,定义修改这个数据的方法,这些方法称为 mutation;
mutation 函数的第一个参数是 state 对象,所有的数据都定义state中,在 mutation 函数中通过 state 可以修改 上面 state 中的数据;

  • 注意 mutation 中修改数据只能使用同步的方式,不能再异步的操作中更新数据;如果有异步就需要使用action;

action 也是更新数据的方式,action 不同于 mutation 的是 action 可以使用异步,但是更新数据仍然需要 commit 对应的 mutation;

二、使用 Vuex 的步骤

  • 在此之前,你需要安装 Vuex,如果是 vue-cli ,在初始化项目时选择 Vuex
  1. 把需要放到 Vuex 中的数据存在 state 里面
  2. 创建修改这些数据的 mutation
  3. 如果这些数据需要异步更新,则创建对应的 action , 并且在 action 中通过 commit mutation 的方式更新数据
  4. 最后导出 Vuex 的 stroe 实例,并且为 Vue 的根实例配置 store 属性,配置 store 属性后,在 Vue 实例中可以通过 this.$store 访问 store
  5. 在整个 Vue 的应用中,任何地方需要使用该数据,通过 this.$store.state.属性名 的方式获取数据;
  6. 如果需要更新这些数据可以通过调用 this.$store.commit(mutation函数名),如果是异步更新,需要 this.$store.dispatch(action名)

三、示例:

  • store.js
  1. import Vue from 'vue'
  2. import Vuex from 'vuex'
  3. Vue.use(Vuex)
  4. export default new Vuex.Store({
  5. state: {
  6. // state 就是数据,如果数据定义在 state 中组件中如果要使用这个数据 this.$store.state.属性名 的方式获取
  7. num: 15
  8. },
  9. mutations: {
  10. // state 中的数据不能被直接修改,如果要修改这些数据,需要使用 mutation,注意 mutation 中不能使用异步更新 state
  11. add (state) {
  12. // mutation 函数的第一个参数是 state 对象,所有的数据都定义 state 中,在 mutation 函数中通过 state 可以修改 上面 state 中的数据;
  13. state.num++
  14. },
  15. addNum (state, payload) {
  16. // mutation 的第二个参数 payload 是在 commit mutation 时传入的参数
  17. state.num += payload
  18. }
  19. },
  20. actions: {
  21. // action 可以使用异步,但是更新数据仍然需要 commit 对应的 mutation
  22. asyncAdd(context) {
  23. setTimeout(() => {
  24. context.commit('add')
  25. }, 1000)
  26. }
  27. }
  28. })
  • App.vue
  1. <template>
  2. <div id="app">
  3. <div id="nav">
  4. <router-link to="/">Home</router-link> |
  5. <router-link to="/about">About</router-link>
  6. <h1>someNum: {{$store.state.num}}</h1>
  7. </div>
  8. <router-view/>
  9. </div>
  10. </template>
  11. <script>
  12. import Bus from './bus'
  13. export default {
  14. name: 'App',
  15. data () {
  16. return {}
  17. }
  18. }
  19. </script>
  • Home.vue
  1. <template>
  2. <div class="home">
  3. <img alt="Vue logo" src="../assets/logo.png">
  4. <div>
  5. <button @click="add">给上面的sumNum加加</button>
  6. <button @click="add2">给上面的sumNum加2</button>
  7. </div>
  8. </div>
  9. </template>
  10. <script>
  11. export default {
  12. name: 'home',
  13. methods: {
  14. add () {
  15. this.$store.commit('addOne') // commit mutation
  16. this.$store.dispatch('asyncAdd') // dispatch action
  17. },
  18. add2 () {
  19. this.$store.commit('addNum', 2) // 传递 payload
  20. }
  21. }
  22. }
  23. </script>

四、mapState/mapMutations/mapActions

如果使用很多个 store 中的数据和方法时通过 this.$store 的方式很不方便,Vuex 提供了三个对应的方法用于批量获取多个 state、mutation、action;

  1. <script>
  2. // @ is an alias to /src
  3. import HelloWorld from '@/components/HelloWorld.vue'
  4. // import Bus from '../bus'
  5. import { mapState, mapMutations, mapActions } from 'vuex'
  6. export default {
  7. name: 'home',
  8. methods: {
  9. updateX (e) {
  10. this.$store.commit('changeX', e.target.value)
  11. },
  12. updateSex(e) {
  13. this.$store.commit('changeSex', e.target.value)
  14. },
  15. add () {
  16. this.addOne() // 等效于 this.$store.commit('addOne')
  17. },
  18. add2 () {
  19. this.addNum(2); // 等效于 this.$store.commit('addNum', 2)
  20. },
  21. add3 () {
  22. this.asyncAdd2(); // 等效于 this.$store.dispatch('asyncAdd')
  23. },
  24. ...mapMutations(['addOne', 'addNum']),
  25. ...mapActions({
  26. asyncAdd2: 'asyncAdd' // 重命名
  27. })
  28. },
  29. computed: {
  30. ...mapState(['num'])
  31. },
  32. components: {
  33. HelloWorld
  34. }
  35. }
  36. </script>
  • 通过 mapState、mapMutations、mapActions 方法结合展开运算符,可以把对应的数据和方法添加到computed 和 methods 中的,这些数据方法都可以通过 this 实例访问

五、在项目中使用 vuex

  • 个人建议,如果这个数据被多处依赖了最好使用 Vuex,而不是所有的数据都交给 Vuex 托管;

  • Vue 书城首页的 轮播图、热门图书使用 Vuex

  • store.js

  1. import Vue from 'vue'
  2. import Vuex from 'vuex'
  3. Vue.use(Vuex)
  4. import * as homeActions from './model/home'
  5. export default new Vuex.Store({
  6. state: {
  7. slides: [],
  8. hotBooks: []
  9. },
  10. mutations: {
  11. fillSides (state, payload) {
  12. state.slides = payload
  13. },
  14. fillHotBooks (state, payload) {
  15. state.hotBooks = payload
  16. }
  17. },
  18. actions: {
  19. async getSliders ({ commit }) {
  20. commit('fillSides', await homeActions.getSliders())
  21. },
  22. async getHotBooks ({ commit }) {
  23. commit('fillHotBooks', await homeActions.getHot())
  24. }
  25. }
  26. })
  • Home.vue
  1. <template>
  2. <div >
  3. <MyHeader>首页</MyHeader>
  4. <div class="content">
  5. <swiper :sliders="slides"></swiper>
  6. <div class="container">
  7. <h2>热门图书</h2>
  8. <ul class="container">
  9. <li v-for="(book, index) in hotBooks" :key="index">
  10. <img :src="book.bookCover" alt="">
  11. <b>
  12. {{book.bookName}}
  13. </b>
  14. </li>
  15. </ul>
  16. </div>
  17. </div>
  18. </div>
  19. </template>
  20. <script>
  21. // @ is an alias to /src
  22. import MyHeader from '@/components/MyHeader.vue'
  23. import swiper from '@/components/Swiper.vue'
  24. // import { getSliders, getHot } from '../model/home'
  25. import { mapState, mapActions } from 'vuex'
  26. export default {
  27. name: 'home',
  28. data () {
  29. return {}
  30. },
  31. computed: {
  32. // slides 和 hotBooks 通过 mapState 从 Vuex store 中获取
  33. ...mapState(['slides', 'hotBooks'])
  34. },
  35. created () {
  36. // 通过 Vuex 获取图书和轮播信息
  37. this.getSliders(); // this.$store.dispatch('getSliders')
  38. this.getHotBooks(); // this.$store.dispatch('getHotBooks')
  39. },
  40. methods: {
  41. ...mapActions(['getSliders', 'getHotBooks'])
  42. },
  43. components: {
  44. MyHeader,
  45. swiper
  46. }
  47. }
  48. </script>
  49. <style scoped lang="less">
  50. .container {
  51. box-sizing: border-box;
  52. overflow-x: hidden;
  53. h2 {
  54. padding-left: 30px;
  55. }
  56. ul li {
  57. float: left;
  58. width: 50%;
  59. margin: 20px 0;
  60. img {
  61. display: block;
  62. }
  63. b {
  64. padding-left: 30px;
  65. }
  66. }
  67. }
  68. </style>