Vue中的Vuex详解

235 阅读2分钟

什么是Vuex?

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式, 采用集中式存储管理应用的所有组件的状态,解决多组件数据通信。

要点:

  1. vue官方搭配,专属使用 (类似于:vue-router),有专门的调试工具
  2. 集中式管理数据状态方案 (操作更简洁)data() { return { 数据, 状态 }}
  3. 数据变化是可预测的 (响应式)

vue官方提供的独立于组件体系之外的,管理公共数据的工具

Vuex中有5个内容需要学习:

  1. state: 统一定义公共数据(类似于data(){return {a:1, b:2,xxxxxx}})
  2. mutations : 使用它来修改数据(类似于methods)
  3. getters: 类似于computed(计算属性,对现有的状态进行计算得到新的数据-------派生 )
  4. actions: 发起异步请求
  5. modules: 模块拆分
  6. 其中最为重要的内容是state和mutations,需要重点掌握

 Vuex的使用步骤:

  1. 下载Vuex

 npm i vuex  或者  yarn add vuex

     2.  在src目录下新建一个统一的文件夹store,并在store中创建index.js

     3.  在store/index.js 中放置具体的代码,具体如下:

import Vue from "vue"
import Vuex from "vuex"

Vue.use(Vuex)

export default new Vuex.Store({
  state: {},
  mutations: {},
  getters: {},
  actions: {},
  modules: {}
})

注意: 别忘了向Vue实例中注入store

在src/main.js中:

  1. 导入store
  2. 并注入Vue实例
import store from './store'

new Vue({
  render: h => h(App),
  store
}).$mount('#app')

1. state 

vuex用它来保存公共数据

格式:

new Vuex.store({
  state: {
   属性名: 属性值 
  }
})

使用公共数据格式:

在组件中,通过this.$store.state.属性名来访问。

在模板中,则可以省略this而直接写成: {{$store.state.属性名}}

示例:

export default new Vuex.Store({
  state: {
    num: 8
  }
)}

接下来在模板中获取并使用这个公共数据

<template>
  <div>
    {{ $store.state.num }}  // 页面中会打印出 8 
  </div>
</template>

2.mutations

mutations主要用于修改state中的公共数据

定义格式:

nwe Vue.State({
    mutations: {
     // 每一项都是一个函数,可以声明两个形参
  	    mutation名1function(state ,[载荷]) {
 
        },
        mutation名2function(state ,[载荷]) {
  
        }
    }
})

每一项都是一个函数,可以声明两个形参:

  • 第一个参数是必须的,表示当前的state。在使用时不需要传入
  • 第二个参数是可选的,表示载荷,是可选的。在使用时要传入的数据

使用格式:

this.$store.commit('mutation名', 实参,给第二个参数)

这里的commit是固定的方法。

示例:

export default new Vuex.Store({
  state: {
    num: 8
  },
  mutations: {
    dobuleNum: function(state) {
      state.num *= 2
    },
  }
})

接下来在App.vue中定义一个button按钮,点击来让num翻倍,

 注意: 在模板中,不用加'this' -----   $store.commit(‘mutations名’)

          在组件中,需要加this调用

<template>
  <div>
    {{ $store.state.num }}
     <!-- 使用全局mutations -->
    <button @click="$store.commit('dobuleNum')">全局mutations修改数据</button>
  </div>
</template>

3.getters

getters的作用是在state中的数据的基础上,进一步对数据进行加工得到新数据。(与组件中computed(计算属性)一样)

定义格式:

new Vuex.store({
  // 省略其他...
  getters: {
    // state 就是上边定义的公共数据state
    getter的名字1: function(state) {
      return 要返回的值
    }
  }
})

使用格式:

在模板中通过:$store.getters.getter的名字 来访问

示例:

export default new Vuex.Store({
  state: {
    num: 8
  },
getters: {
    tenfold(state) {
      return state.num * 10
    }
  }
})

然后再App.vue中直接使用就行了

<template>
  <div>
     <p>使用全局getters:{{ $store.getters.tenfold }}</p>
  </div>
</template>

4.actions

我们可以使用Action来修改state,这一点是类似于 mutation的,不同在于:

  • action中可以通过调用 mutation来修改state,而不是直接变更状态。
  • action 可以包含任意异步(例如ajax请求)操作。 

定义格式:

new Vuex.store({
  // 省略其他...
  actions: {
    // context对象会自动传入,它与store实例具有相同的方法和属性
    action的名字: function(context, 载荷) {
      // 1. 发异步请求, 请求数据
      
      // 2. commit调用mutation来修改/保存数据
      
      // context.commit('mutation名', 载荷)
    }
  }
})

使用格式:

在组件中通过`this.$store.dispatch('actions的名字', 参数)`来调用action

在模块中通过`$store.dispatch('actions的名字', 参数)`来调用action

示例:

export default {
  state: {
     books: []
    },

   mutations: {
    setBooks(state, newBooks) {
      state.books = newBooks;
        }
    },
  actions: {
    getBooks(context) {
      axios({
        method: "get",
        url:
         "https://www.fastmock.site/mock/37d3b9f13a48d528a9339fbed1b81bd5/book/api/books"
      }).then(res => {
        console.log(res);
        context.commit("setBooks", res.data.data);
      })
    }
  }
}

注意: 在state中我定义了一个数组 book:[ ]用来接收获取到的数据 , 接收到数据res后, 想赋值给book数组, 但是要记住不能直接进行赋值,必须要在mutations中定义一个赋值的函数, 在actions获取到数据.调用mutations中的函数,进行赋值.

接下来在App.vue中创建一个button按钮,点击按钮触发actions,发起axios请求获取数据,并赋值给book数组

<template>
    <div>
          // 触发actions 并发起axios请求数据
        <button @click="$store.dispatch('getBooks')">
          点击获取图书信息
        </button>
    </div>
</template>

5.modules

modules用来拆分index.js中的代码, 减少index.js中代码的繁多和体积,让index.js中更简洁,把相同的需要的数据和方法都提取到同一个js中

modules默认namespaced: 为false ,设置true后.,在组件中使用拆分到modules的数据和方法要加"模块名"

首先:在store文件夹下建立modules文件夹,然后再modules文件夹下自定义一个getBooks.js文件,然后将index.js中的 state、mutations、getters、actions中的相关代码剪切到getBooks.js文件中

import axios from "axios";

export default {
  namespaced: true,
  state: {
    skills: ["vue", "html", "js"],
    nubs: 20,
    books: []
  },
  mutations: {
    setBooks(state, newBooks) {
      state.books = newBooks;
    },
    change(state, newSkills) {
      state.skills = newSkills;
    }
  },
  getters: {
    sum(state) {
      return state.nubs + 10;
    }
  },
  actions: {
    getBooks(context) {
      axios({
        method: "get",
        url:
          "https://www.fastmock.site/mock/37d3b9f13a48d528a9339fbed1b81bd5/book/api/books"
      }).then(res => {
        console.log(res);
        context.commit("setBooks", res.data.data);
      });
    }
  }
}

抽离到getBooks.js中后,在index.js中引入,并添加到modules对象中

import Vue from 'vue'
import vuex from 'vuex'
import getBooks from './modules/getBooks.js'

Vue.use(vuex)

export default new vuex.Store({
    modules: {
        getBooks
    }
})

注意: 抽离后.组件中调用的方式就变了, 还记得我们加了namespaced: true这句话, 加了之后,引用数据和方法时都必须要加上模块名了

使用格式:

访问模块中的数据,要加上模块名

获取数据项:  {{store.state.模块名.数据项名}}\ 获取getters: {{store.getters['模块名/getters名']}}

访问模块中的mutations/actions:

  • 如果namespaced为true,则需要额外去补充模块名
  • 如果namespaced为false,则不需要额外补充模块名

store.commit(mutations)         //namespacedfalse store.commit('mutations名')                 // namespaced为false\ store.commit('模块名/mutations名')     // namespaced为true

 代码更加直观,举几个例子:

// 例子: 
1. 原先写法:  <p>{{$store.getters.sum}} </p>  //  30
2. 抽离后的写法: <p>{{$store.gerters['getBooks/sum']}} </p> // 30
 
     // 这里我都列举一下其他三种的方式
     // state: 
<p>{{ $store.state.getBooks.skills[0] }}</p>
     // mutations:
<button @click="$store.commit('getBooks/change',['css','小程序','react'])"></button>
     // actions:
<button @click="$store.dispatch('getBooks/getBooks')"></button>

补充: 如果要修改state/mutations/getters/actions名字,例如:可以这样写 $store.commit(‘模块名,{新名字,旧名字}’) ,其他的格式都类似如此

在使用modules时,建议都给加上namespaced!

Vuex的辅助函数map系列用法汇总

mapState/mapMutations/mapGetters/mapActions
用来优化访问的方式, 普通的写法太麻烦了,利用vuex内置的方法,可以简洁的引用vuex中的数据和方法

1. mapState函数

将state中的变量映射到当前组件中使用
使用步骤,代码如下:

// index.js中
export default new vuex.Store({
    state: {
        num: 10
    }
})



// App.vue中
<template>
  <div>
    {{ num }} // 10
  </div>
</template>

<script>
//  导入辅助函数mapState,它是在vuex中定义的一个工具函数。
import { mapState } from 'vuex'

export default {
  computed: {
    ...mapState(['num'])
    // 如果需要改名字的话
    ...mapState({ '新名字': '旧名字' })
  }
}
</script>

computed:{ …mapState() } 这里的…是对象的展开运算符,整体来看是对象的合并

2. mapMutations

// 在state中定义一个变量为a:10
// App.vue组件中
<template>
  <div>
    {{a}}
    <button @click="add">点击+1</button>
  </div>
</template>

<script>
import { mapMutations, mapState } from 'vuex'
export default {
  computed: {
    ...mapState(['a'])
  },
  methods: {
    ...mapMutations(['add'])
  },
}
</script>




// index.js中
export default new vuex.Store({
    state: {
        a: 10,

    },
    mutations: {
        add: function (state) {
            state.a += 1
        }
    },
})

以上列举了mapState和mapMutations的语法,其他两个(mapGetters和mapActions)用法和前两个都是一样的,自行体会吧

接下来讲一下在全局中和modules情况下使用map系列的用法:

先上图:

 如何使用全局state

  • 直接使用: this.$store.state.xxx; 
  • map辅助函数:
computed: { 
  ...mapState(['xxx']), 
  ...mapState({'新名字': 'xxx'})
}

如何使用modules中的state

  • 直接使用: this.$store.state.模块名.xxx;
  • map辅助函数:
computed: { 
  ...mapState('模块名', ['xxx']), 
  ...mapState('模块名', {'新名字': 'xxx'})
}

如何使用全局getters

  • 直接使用:this.$store.getters.xxx
  • map辅助函数:
computed: { 
  ...mapGetters(['xxx']), 
  ...mapGetters({'新名字': 'xxx'})
}

如何使用modules中的getters

  • 直接使用: this.$store.getters.模块名.xxx
  • map辅助函数:
computed: { 
  ...mapGetters('模块名', ['xxx']), 
  ...mapGetters('模块名',{'新名字': 'xxx'})
}

如何使用全局mutations

  • 直接使用:this.$store.commit('mutation名', 参数)
  • map辅助函数:
methods: { 
  ...mapMutations(['mutation名']), 
  ...mapMutations({'新名字': 'mutation名'})
}

如何使用modules中的mutations(namespaced:true)

  • 直接使用: this.$store.commit('模块名/mutation名', 参数)
  • map辅助函数:
methods: { 
  ...mapMutations('模块名', ['xxx']), 
  ...mapMutations('模块名',{'新名字': 'xxx'})
}

如何使用全局actions

  • 直接使用:this.$store.dispatch('action名', 参数)
  • map辅助函数:
methods: { 
  ...mapActions(['actions名']), 
  ...mapActions({'新名字': 'actions名'})
}

如何使用modules中的actions(namespaced:true)

  • 直接使用: this.$store.dispatch('模块名/action名', 参数)
  • map辅助函数:
methods: { 
  ...mapActions('模块名', ['xxx']), 
  ...mapActions('模块名',{'新名字': 'xxx'})
}

今天就写到这里,谢谢各位观众老爷的观看,如果觉得写的还可以,记得一键三连哦,这篇博客前面还发过vuex核心API的关系图,和此文一起食用效果更佳哦

每日一汤:

业精于勤,荒于嬉;行成于思,毁于随。——韩愈