Vuex入门教程

简介: 组件化应用构建是Vue的特点之一,因此我们在Vue的实际开发过程中会经常需要封装自定义组件,以提高开发的效率。 而组件在大部分情况下并不会孤立的存在,它必然会与父组件和兄弟组件产生数据的交互。所以在这里为大家总结两种组件数据交互的方式:EventBus和利用Vuex框架进行状态管理。

前言

组件化应用构建是Vue的特点之一,因此我们在Vue的实际开发过程中会经常需要封装自定义组件,以提高开发的效率。 而组件在大部分情况下并不会孤立的存在,它必然会与父组件和兄弟组件产生数据的交互。所以在这里为大家总结两种组件数据交互的方式:EventBus和利用Vuex框架进行状态管理。

上一篇文章介绍了如果通过EventBus在Vue自定义组件中进行事件传递(Vue自定义组件事件传递:EventBus部分),而EventBus比较适合在中小型项目中使用;如果我们需要开发一套大型的项目,这时候我们应该考虑使用Vuex框架来统一进行数据状态管理。

为了方便读者理解Vuex的基本使用,我将继续沿用上一篇文章的案例(显然在单页和中小型应用中,Vuex显得不太必要),为大家介绍Vuex的使用。

为什么有EventBus还要使用Vuex

为什么我们有EventBus了,还要使用Vuex?

其实在Vuex官网就有很好的解释:Vuex官网对Vuex是什么的解释

对于Vuex是什么,我的理解是:

Vuex是数据状态管理框架,主要做到了数据和视图层的解耦

而已经有EventBus我们为什么还要使用Vuex这个问题上,我的理解是:

我们仔细思考一下,当我们使用EventBus时,我们A控件给B控件和C控件传递数据后,B控件经过处理又要修改传递过来数据,继续通知A控件和C控件修改数据,这样在C文件中就要多处监听A和B的EventBus。这样当有关于数据传递有bug时,我们就很难追溯到底是哪一个环节出了问题。

伴随着我们业务的复杂程度变高,视图的复杂度也会随之升高,所以此时通过Vuex来对数据和视图层解耦就显得十分必要。

案例介绍(与上一篇"Vue自定义组件事件传递:EventBus部分"案例一致,可跳过)

本章节会有大量的代码示例,为了让读者阅读轻松,做如下目录和组件介绍。本章节主要运用了两个子组件和一个父组件。

子组件文件名:SearchInput.vueSearchItem.vue

父组件文件名:StateView.vue

目录结构展示:

1、SearchInput.vue

组件介绍:一个输入框,它会onInput方法去监听输入内容,并调用方法,将输入框内的数据传递出去。

代码展示:

<template>
  <div>
    <input placeholder="搜索内容"  v-model="searchContent"/>
  </div>
</template>

<script type="text/ecmascript-6">
  export default{
    data(){
      return{
        searchContent:""
      }
    },
    props:{

    }
  }
</script>

<style lang="stylus" rel="stylesheet/stylus">

</style>


复制代码

SearchItem.vue

组件介绍:一个span,它主要用来接收父组件传递的内容和接收同胞组件输入框传递的内容,并进行展示。

代码示例:

<template>
    <span class="item">
      {{content}}
    </span>
</template>

<script type="text/ecmascript-6">
  export default{
    data(){
      return{
        content:this.itemContent
      }
    },
    props:{
      itemContent:{
        type:String,
        required:true
      }
    }
  }
</script>

<style lang="stylus" rel="stylesheet/stylus">
  .item
    background #f4f4f4
    padding 3px 5px
    box-sizing border-box
    display inline-block
    cursor pointer
</style>

复制代码

StateView.vue

组件介绍:父组件,主要展示页面和加载子组件

代码示例:

<template>
  <div>
    <search-view></search-view>
    <div>
      <search-item :itemContent="content"/>
      <search-item itemContent="热门搜索2"/>
      <search-item itemContent="热门搜索3"/>
    </div>
    <div>{{content}}</div>

  </div>
</template>

<script type="text/ecmascript-6">
import searchView from '../components/SearchInput.vue'
import searchItem from '../components/SearchItem.vue'

export default{
  data () {
    return {
      content:"接收输入框的值"
    }
  },
  components: {
    searchView,
    searchItem
  }
}

</script>

<style lang="stylus" rel="stylesheet/stylus">

</style>

复制代码

正文

全局注册Vuex

在开始介绍Vuex的内容前,我们需要在全局中注册一个Vuex对象。

查看我们的目录结构:

1、创建Store文件

我们创建了一个store文件夹,专门存放各种类型的store文件。

在这我们创建了SearchStore.js文件。它就是我们接下来主要的主角,关于Vuex的State、Getter、Mutation、Action都会围绕它来展开。

SearchStore.js代码如下:

import Vue from 'vue'
import vuex from 'vuex'
Vue.use(vuex)
export default new vuex.Store({
})

复制代码

2、在main.js中注入SearchStore.js

main.js

import Vue from 'vue'
import App from './App'
import router from './router'
import vueResource from 'vue-resource'
import searchStore from './store/SearchStore'   //引入SearchStore.js
Vue.use(vueResource)
Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  store:searchStore,           //注入SearchStore
  components: { App },
  template: '<App/>'
})

复制代码

我们在main.js中引入了SearchStore.js文件,并在Vue根组件中注入了SearchStore.js文件,因此所有的子组件都能访问到了这个SearchStore.js这个Store。

State

State就像一个全局的数据仓,里面存放着数据源

这句话怎么理解呢?

不急,我们看一个Demo例子,你就会对State一下就理解了。

我们修改SearchStore.js的文件,给它加一个数据仓,即State:

import Vue from 'vue'
import vuex from 'vuex'
Vue.use(vuex)
export default new vuex.Store({
  state:{
    searchContent:"这是Store的searchContent内容"
  }
})

复制代码

然后我们在StateView.jsSearchInput.js的mounted()生命周期中去打印SearchStore.js中的searchContent:

StateView

<template>
  <div>
    <search-view></search-view>
    <div>
      <search-item :itemContent="content"/>
      <search-item itemContent="热门搜索2"/>
      <search-item itemContent="热门搜索3"/>
    </div>
    <div>{{content}}</div>

  </div>
</template>

<script type="text/ecmascript-6">
import searchView from '../components/SearchInput.vue'
import searchItem from '../components/SearchItem.vue'
import searchEvent from '../event/SearchEvent'
export default{
  data () {
    return {
      content:""
    }
  },
  mounted(){
    console.log("StateView======"+this.$store.state.searchContent)   //打印SearchStore中的searchContent
  },
  components: {
    searchView,
    searchItem
  }
}

</script>

<style lang="stylus" rel="stylesheet/stylus">

</style>

复制代码

SearchInput.js

<template>
  <div>
    <input placeholder="搜索内容" @input="sendEvent" v-model="searchContent"/>
  </div>
</template>

<script type="text/ecmascript-6">
  import searchEvent from '../event/SearchEvent'
  export default{
    data(){
      return{
        searchContent:""
      }
    },
    mounted(){
      console.log("SearchInput======"+this.$store.state.searchContent)
    },
    props:{

    }
  }
</script>

<style lang="stylus" rel="stylesheet/stylus">

</style>

复制代码

控制台输出结果

怎么样,对state有感觉了嘛?

State的感受结论:

1、state是一个数据存储的仓库,所有的数据源都会存放在这里,就类似组件中的data。

2、我们可以明显感觉到,我们在SearchStore.js的state中的数据,可以在任意组件中通过this.$store.state访问到,而不是像使用EventBus一样,需要到处去调用$emit$on方法去监听数据和拷贝数据副本,做到了数据和视图层的解耦。

Getter

Getter 用来在获取数据源的时候,对数据源进行一定的加工后再返回。

接下来我们在SearchStore.js中加入Getter,来感受下上面这句话所表达的意思。

SearchStore.js

import Vue from 'vue'
import vuex from 'vuex'
Vue.use(vuex)
export default new vuex.Store({
  state:{
    searchContent:"这是Store的searchContent内容"
  },
  getters:{     //在这里加入getter
    getSearchContent(state){       
    //我们定义了一个getter函数,叫做gerSearchContent,它有一个参数,这个参数保存了state对象。
    //即SearchStore.js的state对象
      return "加工后的searchContent===="+state.searchContent     //模拟加工数据,并将加工后的数据返回
    }
  }
})
复制代码

在这里,我们在Store的getters中定义了一个getSearchContent的函数,这个函数接收一个参数,这个参数就是我们的state对象,在这个例子中,即:SearchStore.js的state对象。

然后我们模拟了数据加工的过程,返回一个数据加工后的数据。

接下来,让我们在StateView的mounted()生命周期中,打印这个getter。

StateView.js

<template>
  <div>
    <search-view></search-view>
    <div>
      <search-item :itemContent="content"/>
      <search-item itemContent="热门搜索2"/>
      <search-item itemContent="热门搜索3"/>
    </div>
    <div>{{content}}</div>

  </div>
</template>

<script type="text/ecmascript-6">
import searchView from '../components/SearchInput.vue'
import searchItem from '../components/SearchItem.vue'
import searchEvent from '../event/SearchEvent'
export default{
  data () {
    return {
      content:""
    }
  },
  mounted(){
    console.log("StateView======"+this.$store.state.searchContent)   //打印SearchStore中的searchContent
    console.log("StateView======"+this.$store.getters.getSearchContent)  //打印SearchStore中的getSearchContent
  },
  components: {
    searchView,
    searchItem
  }
}

</script>

<style lang="stylus" rel="stylesheet/stylus">

</style>
复制代码

控制台输出的结果

Getter的感受结论

1、getter主要用于在获取数据时,对数据进行加工后返回。

2、与EventBus相比,通过Vuex的Getter,我们可以在Store中对数据做统一加工处理,利于日后的项目维护。

Mutation

对数据仓中的数据,进行修改。Mutation只支持同步方法

同样,我们通过在SearchStore.js加入Mutation来感受Mutation的作用

SearchStore.js

import Vue from 'vue'
import vuex from 'vuex'
Vue.use(vuex)
export default new vuex.Store({
  state:{
    searchContent:"这是Store的searchContent内容"
  },
  getters:{
    getSearchContent(state){
      return "加工后的searchContent===="+state.searchContent
    }
  },
  mutations:{
    changeSearchContent(state,payload){
      /**
       * 传递单个数据参数
       */
      state.searchContent=payload     //修改state的searchContent数据
    },
    changeSearchContentByObject(state,payload){
      /**
       * 有时候我们需要再payload传递多个数据参数时,我们可以通过对象的方式传递
       */
      state.searchContent="通过Object对象来修改数据:"+payload.searchContent
    }
  }
})
复制代码

注意:上面我们定义了两个Mutation方法,他们唯一的不同是一个接受的payload是单个参数;而在需要传递多个参数时,我们可以在payload中传递对象的方式,去传递多个参数。

接下来我们依旧修改StateView.js。

StateView.js

<template>
  <div>
    <search-view></search-view>
    <div>
      <search-item :itemContent="content"/>
      <search-item itemContent="热门搜索2"/>
      <search-item itemContent="热门搜索3"/>
    </div>
    <div>{{content}}</div>

  </div>
</template>

<script type="text/ecmascript-6">
import searchView from '../components/SearchInput.vue'
import searchItem from '../components/SearchItem.vue'
import searchEvent from '../event/SearchEvent'
export default{
  data () {
    return {
      content:""
    }
  },
  mounted(){
    console.log("StateView======"+this.$store.state.searchContent)   //打印SearchStore中的searchContent
    console.log("StateView======"+this.$store.getters.getSearchContent)  //打印SearchStore中的getSearchContent
    
    //通过this.$store.commit()方法修改state的searchContent参数
    //在payload中传递单个参数
    this.$store.commit("changeSearchContent","StateView中修改后的SearchContent")
    console.log("StateView======"+this.$store.state.searchContent)
    
    //当需要传递多个参数时,我们可以在payload中以对象的形式传递
    this.$store.commit("changeSearchContentByObject",{"searchContent":"StateView中修改后的SearchContent","testVal":"testVal"})
    console.log("StateView======"+this.$store.state.searchContent)
  },
  components: {
    searchView,
    searchItem
  }
}

</script>

<style lang="stylus" rel="stylesheet/stylus">

</style>
复制代码

StateVie.js中,我们通过this.store.commit()方法去调用mutations中的方法,它接收两个参数:

1、第一个参数传递mutations中对应的方法名 2、第二个参数传递payload,即要传递的数据

控制台输出的结果

Mutation的感受结论:

1、通过Mutation我们可以对数据仓中的数据进行修改,我们可以在组建中通过调用this.$store.commit()方法去调用对应的Mutation去修改数据。

2、Mutation中只能执行同步的方法,如果需要执行异步方法,我们要使用接下来即将登场的Action。

Action

Action和Mutation类似,他们之处在于:1、Action 提交的是 mutation,而不是直接变更状态。2、Action 可以包含任意异步操作。

话不多说,我们直接修改SearchStore.js文件:

SearchStore.js

import Vue from 'vue'
import vuex from 'vuex'
Vue.use(vuex)
export default new vuex.Store({
  state:{
    searchContent:"这是Store的searchContent内容"
  },
  getters:{
    getSearchContent(state){
      return "加工后的searchContent===="+state.searchContent
    }
  },
  mutations:{
    changeSearchContent(state,payload){
      /**
       * 传递单个数据参数
       */
      state.searchContent=payload     //修改state的searchContent数据
    },
    changeSearchContentByObject(state,payload){
      /**
       * 有时候我们需要再payload传递多个数据参数时,我们可以通过对象的方式传递
       */
      state.searchContent="通过Object对象来修改数据:"+payload.searchContent
    }
  },
  actions:{
    changeSearchContentByAction(context,payload){
      /**
       * 模拟异步的过程,2000毫秒后通过commit()方法执行mumations中的changeSearchContentByObject方法改变数据
       * 同样,payload可以是单数据或通过对象的方式,传递多个数据
       * 这里只举例通过对象传递多数据的情况
       */
      setTimeout(()=>{
        context.commit("changeSearchContentByObject",payload)
      },2000)
    }
  }
})
复制代码

在这里我们模拟了异步的过程,通过调用setTimeOut()函数,在2000毫秒后通过调用commit()函数来改变searchContent数据。

接下来我们来修改StateView.js来调用我们刚刚写的changeSearchContentByAction

StateView.js

<template>
  <div>
    <search-view></search-view>
    <div>
      <search-item :itemContent="content"/>
      <search-item itemContent="热门搜索2"/>
      <search-item itemContent="热门搜索3"/>
    </div>
    <div>{{content}}</div>

  </div>
</template>

<script type="text/ecmascript-6">
import searchView from '../components/SearchInput.vue'
import searchItem from '../components/SearchItem.vue'
import searchEvent from '../event/SearchEvent'
export default{
  data () {
    return {
      content:""
    }
  },
  mounted(){
    console.log("StateView======"+this.$store.state.searchContent)   //打印SearchStore中的searchContent
    console.log("StateView======"+this.$store.getters.getSearchContent)  //打印SearchStore中的getSearchContent
    
    //通过this.$store.commit()方法修改state的searchContent参数
    //在payload中传递单个参数
    this.$store.commit("changeSearchContent","StateView中修改后的SearchContent")
    console.log("StateView======"+this.$store.state.searchContent)
    
    //当需要传递多个参数时,我们可以在payload中以对象的形式传递
    this.$store.commit("changeSearchContentByObject",{"searchContent":"StateView中修改后的SearchContent","testVal":"testVal"})
    console.log("StateView======"+this.$store.state.searchContent)
    
    //通过Action修改数据
    this.$store.dispatch("changeSearchContentByAction",{"searchContent":"action修改后的数据"})
    setTimeout(()=>{
      console.log("5000毫秒后StateView======"+this.$store.state.searchContent)
    },5000)
    
  },
  components: {
    searchView,
    searchItem
  }
}

</script>

<style lang="stylus" rel="stylesheet/stylus">

</style>
复制代码

我们在StateView.js中,通过this.$store.dispatch()函数去调用action,该函数接收两个参数: 1、Store中Action对应的方法名 2、要传递的数据

** 控制台输出结果**

Action的感受结论:

1、Action和Mutation类似,它只是能够处理异步的情况,最终通过commit()函数调用Mutation去修改数据。

2、通过this.$store.dispatch()去调用Action的方法。

Module

可以将Store模块化,然后通过Module整合在一起。

我们如果将所有的数据都写入一个Store的state中,这时候我们获取,修改数据。随着项目不断变大,我们这个Store中的state和Mutation、Getter、Action的数量和Store的代码行数就会爆炸性的增加,使得我们的Store变得维护困难。

这时候,我们希望把Store模块化,例如不同子组件的数据抽取出来写成单独的一个Store。

这时候我们就需要通过Module整合各个模块,然后在将Store挂在在根组件下。

现在我们不再以SearchStore举例了,我们创建3个新的Store文件

1、index.js Store,主要负责整合所有的Store模块。

2、SearchInputStore.js, 主要负责输入框子组件SearchInput.js的数据维护

3、SearchItemStore.js,主要负责SearchItem.js子组件的数据维护

首先我们来看SearchInputStore.js和SearchItemStore.js:

SearchInputStore.js

export default {
  state: {
    searchContent:""
  },
  getters:{
    getSearchContent(state){
      return state.searchContent;
    }
  },
  mutations:{
    changeSearchContent(state,payload){
      state.searchContent=payload
    }
  }
}
复制代码

SearchItemStore.js

export default {
  state: {
    searchHistoryList:[]
  },
  getters:{
    getsearchHistoryList(state){
      return state.searchHistoryList;
    }
  },
  mutations:{
    changesearchHistoryList(state,payload){
      state.searchContent.push(payload)
    }
  }
}
复制代码

这里,我们只是通过export default将SearchInputStore和SearchItemStore输出。

接下来让我们看index.js,它将SearchInputStore.jsSearchItemStore.js整合在一起

index.js

/**
 * Created by Vicky on 2018/6/22.
 */
import Vue from 'vue'
import vuex from 'vuex'
import searchInputStore from './SearchInputStore'
import searchItemStore from './SearchItemStore'
Vue.use(vuex)
export default new vuex.Store({
  modules:{
    searchInputStore:searchInputStore,
    searchItemStore:searchItemStore
  }
})

复制代码

在index中我们首先将SearchInputStore.jsSearchItemStore.js通过import引入,然后在Store的modules中将它们两引入。

接下来我们将index.js挂载在根组件下,我们修改一下main.js:

main.js

import Vue from 'vue'
import App from './App'
import router from './router'
import vueResource from 'vue-resource'
import store from './store/index'   //引用index.js
// import searchStore from './store/SearchStore'
Vue.use(vueResource)
Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  store,    //修改部分,简写
  components: { App },
  template: '<App/>'
})
复制代码

这样我们就成功整合了两个Store模块,需要注意的是接下来我们访问state对象的对象时,需要加上模块名

例如,我们要访问SearchInputStore的searchContent的数据时,我们需要使用this.$store.state.searchInputStore.searchContent进行访问。

namespaced: true

namespaced主要用于提高模块的封装性和复用性

我们会遇到一个情况,我们不同模块的Store中可能会存在相同方法名的Mutation、Getter和Action,这时候我们调用commit()会调用所有相同方法名的Mutation;当我们Getter方法名重复时,会报错;当我们调用dispatch()方法时,所有相同方法名的Action都会被执行。

这明显是不符合常理的,它会对其他模块的数据造成污染和异常。但在一个大型项目中,我们往往是多人协同开发的,所以我们很难避免大家在未告知的情况下,定义了相同名字的Mutation、Getter和Action。

此时,我们就需要namespaced来帮我们忙,以提高模块的封装性和复用性。

接下来我们来修改index.jsSearchInputStore.js文件:

index.js

import Vue from 'vue'
import vuex from 'vuex'
import searchInputStore from './SearchInputStore'
import searchItemStore from './SearchItemStore'
Vue.use(vuex)
export default new vuex.Store({
  state:{
    rootVal:"根节点Store的值"
  },
  getters:{
    getRootVal(state){
      return state.rootVal
    }
  },
  mutations:{
    changeRootVal(state,payload){
        console.log("根节点Store的changeRootVal被调用了")
        state.rootVal=payload
    }
  },
  actions:{
    changeRootValByAction(context,payload){
      console.log("根节点Store的changeRootValByAction被调用了")
      context.commit("changeRootVal",payload)
    }
  },
  modules:{
    searchInputStore:searchInputStore,
    searchItemStore:searchItemStore
  }
})


复制代码

我们给index.js加了State、Getter和Mutation,这是为了在后面演示子Store通过namespaced对数据模块进行封装后,子Store如何去访问根Store的Getter、如果通过commit()调用根Mutation和子Mutation;如何通过dispatch()调用根Action和子Action。

SearchInputStore.js

export default {
  namespaced:true,
  state: {
    searchContent:""
  },
  getters:{
    getSearchContent(state,getters,rootState,rootGetters){
      /**
       * state 代表了当前模块的state
       * getters 代表了当前模块的getters
       * rootState 代表了根Store的state,即index.js的Store,但也包含它的子Store,我们可以通过rootState.searchInputStore访问searchItemStore;通过rootState.rootVal访问根模块的State值。
       * rootGetters 代表了根Store的getters和它子Store的getter方法也会被挂载在rootGetters下,
       * 我们可以通过rootGetters["searchItemStore/getHistoryList"]去访问searchItemStore模块下的getHistoryList
       * 通过rootGetters.getRootVal去访问根模块的getRootVal
       */
      //通过rootState去访问searchItemStore的State数据
      console.log(rootState.searchItemStore.searchHistoryList)
      //通过rootState去访问index.js的State数据
      console.log(rootState.rootVal)

      //通过rootGetters去访问searchItemStore的Getter
      console.log(rootGetters["searchItemStore/getHistoryList"])

      //通过rootGetters.getRootVal去访问根模块的getRootVal
      console.log(rootGetters.getRootVal)

      return state.searchContent;
    }
  },
  mutations:{
    changeSearchContent(state,payload){
      /**
       * state 代表了当前模块的state
       * payload 代表了传入的数据
       */
      //this.commit("searchItemStore/changeHistoryList",payload)调用SearchItemStore根模块的changeHistoryList
      this.commit("searchItemStore/changeHistoryList",payload)
      //通过this.commit("changeRootVal",payload)调用根模块的changeRootVal
      this.commit("changeRootVal",payload)
      console.log("changeSearchContent被调用了")
      state.searchContent=payload
    }
  },
  actions:{
    changeSearchContentByAction(context, payload){

      //调用本模块的changeSearchContent
      context.commit("changeSearchContent",payload)
      //调用本模块的otherAction
      context.dispatch("otherAction",payload)

      //调用根节点的Mutation和Action只需要传入第三个参数对象,{root:true}即可
      context.commit("changeRootVal",payload,{root:true})
      //调用根节点的changeRootValByAction
      context.dispatch("changeRootValByAction",payload,{root:true})
    },
    otherAction(context,payload){
      console.log("otherAction被调用")
    }
  }
}

复制代码

SearchItemStore

export default {
  namespaced:true,
  state: {
    searchHistoryList:["历史记录1"]
  },
  getters:{
    getHistoryList(state){
      return state.searchHistoryList;
    }
  },
  mutations:{
    changeHistoryList(state,payload){
      state.searchHistoryList.push(payload)
    }
  },
  actions:{
    changeHistoryListByAction(context,payload){
      context.commit(changeHistoryList,payload)
    }
  }
}

复制代码

我们通过namespaced:true来对模块进行了封装。

因为SearchInputStoreSearchItemStore被封装了,我们子组件应该如何去访问SearchInputStore呢?SearchInput又如何访问其他被封装模块和根Store的State、Getter、Mutation和Action呢?

子组件如何访问封装的模块(以访问SearchInputStore为例)

访问State

在访问被封装的State数据时,我们只需要做如下改变**(以访问SearchInputStore的searchContent为例)**:

this.$store.state.searchContent改变成this.$store.state.searchInputStore.searchContent

访问Getter

(以访问SearchInputStore的getSearchContent为例):

this.$store.getters.getSearchContent改变成this.$store.getters["searchInputStore/getSearchContent"]

访问Mutation

(以访问SearchInputStore的changeSearchContent为例):

this.commit("changeSearchContent","新数据")改变成this.$store.commit("searchInputStore/changeSearchContent","新数据")

访问Action

(以访问SearchInputStore的changeSearchContentByAction为例):

this.dispatch("changeSearchContentByAction","新数据")改变成this.$store.dispatch("searchInputStore/changeSearchContentByAction","新数据")

封装的模块如何访问其他被封装的模块和根模块(以访问SearchItemStore为例)

先让我们再仔细看下面的SearchInputStore的代码:

SearchInputStore

export default {
  namespaced:true,
  state: {
    searchContent:""
  },
  getters:{
    getSearchContent(state,getters,rootState,rootGetters){
      /**
       * state 代表了当前模块的state
       * getters 代表了当前模块的getters
       * rootState 代表了根Store的state,即index.js的Store,但也包含它的子Store,我们可以通过rootState.searchInputStore访问searchItemStore;通过rootState.rootVal访问根模块的State值。
       * rootGetters 代表了根Store的getters和它子Store的getter方法也会被挂载在rootGetters下,
       * 我们可以通过rootGetters["searchItemStore/getHistoryList"]去访问searchItemStore模块下的getHistoryList
       * 通过rootGetters.getRootVal去访问根模块的getRootVal
       */
      //通过rootState去访问searchItemStore的State数据
      console.log(rootState.searchItemStore.searchHistoryList)
      //通过rootState去访问index.js的State数据
      console.log(rootState.rootVal)

      //通过rootGetters去访问searchItemStore的Getter
      console.log(rootGetters["searchItemStore/getHistoryList"])

      //通过rootGetters.getRootVal去访问根模块的getRootVal
      console.log(rootGetters.getRootVal)

      return state.searchContent;
    }
  },
  mutations:{
    changeSearchContent(state,payload){
      /**
       * state 代表了当前模块的state
       * payload 代表了传入的数据
       */
      //this.commit("searchItemStore/changeHistoryList",payload)调用SearchItemStore根模块的changeHistoryList
      this.commit("searchItemStore/changeHistoryList",payload)
      //通过this.commit("changeRootVal",payload)调用根模块的changeRootVal
      this.commit("changeRootVal",payload)
      console.log("changeSearchContent被调用了")
      state.searchContent=payload
    }
  },
  actions:{
    changeSearchContentByAction(context, payload){

      //调用本模块的changeSearchContent
      context.commit("changeSearchContent",payload)
      //调用本模块的otherAction
      context.dispatch("otherAction",payload)

      //调用根节点的Mutation和Action只需要传入第三个参数对象,{root:true}即可
      context.commit("changeRootVal",payload,{root:true})
      //调用根节点的changeRootValByAction
      context.dispatch("changeRootValByAction",payload,{root:true})
    },
    otherAction(context,payload){
      console.log("otherAction被调用")
    }
  }
}
复制代码
访问State和Getter

先看SearchInputStore的getters:

我们发现getSearchContent接受的参数从state变成了state,getters,rootState,rootGetters,它们的含义如下:

1、state 代表了当前模块的state

2、getters 代表了当前模块的getters

3、rootState 代表了根Store的state,即index.js的Store,但也包含它的子Store,我们可以通过rootState.searchInputStore访问searchItemStore;通过rootState.rootVal访问根模块的State值。

4、rootGetters 代表了根Store的getters和它子Store的getter方法也会被挂载在rootGetters下,我们可以通过rootGetters["searchItemStore/getHistoryList"]去访问searchItemStore模块下的getHistoryList

访问Mutation

(以访问SearchInputStore的changeSearchContent为例):

在SearchInputStore的Mutation中可以通过:

1、this.commit("searchItemStore/changeHistoryList",payload)调用SearchItemStore根模块的changeHistoryList

2、this.commit("changeRootVal",payload)调用根模块的changeRootVal

访问Action

(以访问SearchInputStore的changeSearchContentByAction为例):

在SearchInputStore的Action中可以调用根模块的Mutation和Action,但不能调用其它封装模块的Mutation,否则会报错。

其实这也是有道理的,当我们本模块的Mutation异步执行完毕,想要去修改其他模块的数据,只需要在自己模块的Mutation去调用其他模块的Mutation即可,没有必要再去调用其他模块的Action。

因此我们可以在本模块中通过context.commit()cotext.dispatch()去调用本模块和根模块的Mutation和Action:

1、调用本模块的changeSearchContent:context.commit("changeSearchContent",payload)

2、 调用本模块的otherAction:context.dispatch("otherAction",payload)

3、调用根模块的Mutation和Action只需要传入第三个参数对象,{root:true}即可:context.commit("changeRootVal",payload,{root:true})

4、调用根模块的changeRootValByAction:context.dispatch("changeRootValByAction",payload,{root:true})

最后复盘

1、Vuex是数据和视图层解耦的框架

2、在单页和小项目中没有必要使用Vuex。

3、Vuex中的State就像一个全局的数据仓,里面存放着数据源。

4、Vuex中的Getter 用来在获取数据源的时候,对数据源进行一定的加工后再返回。

5、Vuex中的Mutation用来对数据仓中的数据,进行修改。Mutation只支持同步方法。

6、Vuex中的Action和Mutation类似,他们之处在于:1、Action 提交的是 mutation,而不是直接变更状态。2、Action 可以包含任意异步操作。

7、Vuex中的Module可以将Store模块化,然后通过Module整合在一起。

8、关于...mapGetters、...mapMutation、...mapActions具体不展开,主要是通过es6的语法简化代码书写,可以参考官方文档: Vuex官方文档

9、看完本教程,由于内容较多,建议自己再看一次官方文档,并自己实现一个小Demo

我是大麦,如果喜欢我的文章,请给我一颗小心心。



原文发布时间为:2018年07月01日

作者:丨大麦
本文来源: 掘金  如需转载请联系原作者

相关文章
|
存储 JavaScript 前端开发
vuex入门
vuex入门
60 0
|
存储 JavaScript 数据管理
Vuex从入门到精通
vuex是什么? 官方给出的解释是: Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。 看完官方解释后,你可能对状态管理模式很疑惑,对于状态管理模式,官方给出了详细的解释。 这个状态自管理应用包含以下几个部分: 状态,驱动应用的数据源; 视图,以声明方式将状态映射到视图; 操作,响应在视图上的用户输入导致的状态变化。
63 0
|
存储 JavaScript 数据管理
分分钟学会vue中vuex的应用(入门教程)
分分钟学会vue中vuex的应用(入门教程)
|
存储 JavaScript 安全
简介vuex和pinia
简介vuex和pinia
|
存储 前端开发 JavaScript
【深入浅出 React 和 Redux】 笔记(上)
【深入浅出 React 和 Redux】 笔记(上)
80 0
|
开发工具 git
学习Pinia 第一章(介绍Pinia)
代码扁平化没有模块嵌套,只有 store 的概念,store 之间可以自由使用,每一个store都是独立的
98 0
学习Pinia 第一章(介绍Pinia)
|
存储 监控 JavaScript
vuex快速入门
vuex快速入门
105 0
vuex快速入门
|
缓存
学习Pinia 第七章(pinia插件)
学习Pinia 第七章(pinia插件)
99 0
|
JavaScript API 容器
vuex4快速入门
vuex4快速入门
170 0
|
前端开发 JavaScript
React 入门教程笔记
React 入门教程笔记
82 0