浅谈Vuex的使用

正如Redux一样,当你不知道是否需要Vuex那就是不需要。不要因为想用Vuex而使用它。
用过Vue的人都知道,Vuex是Vue的一个全局状态管理模块,它的作用是多个组件共享状态及数据,当某个组件将全局状态修改时,在绑定了该状态的另一个组件也将响应。实际上可以将Vue理解为一个function,在Vue的作用域中有一个数据代理,在每个Vue的实例中都能对其读和写

我们都知道Vue的数据驱动原理是用Object.defineProperty()进行数据代理,在setter中对数据绑定的view进行异步响应(vue3.0则是使用proxy)
通过查看Vuex源码可知Vuex的核心原理就是在Vue的beforeCreate钩子前混入(mixin)Vuex,并在init中将$store属性注册到Vue中

为了使案例更具体,我这还是简单使用脚手架搭了个项目(可参考另一篇文章),虽然只有两个组件,但是能清晰的理解其用法,我的src目录如下,除了最基础的App.vue和main.js外只有两个组件和一个store


先说明一下两个组件的作用,第一个组件是输入框,在里面输入字符,在二个组件div中显示,就是这么简单
首先我们使用常规方式(EventBus)实现一下,这里只需要在mainjs中创建一个vue实例,然后注册在vue中就可以通过事件emit和on来进行组件通信
main.js

import Vue
  from 'vue'
import App
  from './App'
Vue.prototype.$eventBus = new Vue()

new Vue({
  el: '#app',components: {App},template: '<App/>'
})

inputComp.vue

<template>
  <input type="text" @input="inputHandler" />
</template>

<script>

export default {
  name: "inputComp",methods: {
    inputHandler(e) {
      this.$eventBus.$emit('changeVal',e.target.value)//输入时通过eventBus发送消息
    },},};
</script>

<style
  scoped>
</style>

divComp.vue

<template>
  <div>
    {{
      val
    }}
  </div>
</template>

<script>
export default {
  name: "divComp",data () {
    return {
      val: ''
    }
  },mounted () {
    this.$eventBus.$on('changeVal',(e) => {//监听输入事件通过eventBus传递信息
      this.val = e
    })
  }
}
</script>

<style
  scoped>

</style>

效果如下:


下面我会逐级介绍Vuex在这个案例中如何使用

首先我们先加入state用来存放数据值(类似于组件中的data),在store中添加一个state,并在main中引入store

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

Vue.use(Vuex);
const state = {
  val: ''
}
export default new Vuex.Store({
  state
})

然后在上面的inputHandler中将eventBus换成以下代码

inputHandler(e) {
      this.$store.state.val = e.target.value;
    }

将div标签中的val换成this.$store.state.val,这就是最简单的Vuex使用,仅仅通过修改state从而达到全局状态的目的,如果你的项目并不复杂,这个简单的全局状态就足够了

下一步,我们加入一个mutations,这里我们可以将mutations当成发布/订阅的调度中心,将函数写在mutations中相当于注册了一个事件,在页面中通过emit来触发
在store中新增
mutations

const state = {
  val: ''
}

const mutations = {
  changeVal (state,_val) {
    state.val = _val
  }
}

export default new Vuex.Store({
  state,mutations
})

在inputHandler中调用一下该函数,其中changeVal是mutations中的函数名,通过emit传参达到和发布\订阅效果

this.$store.commit('changeVal',e.target.value)

了解了mutations,我们来看看getters,我们都知道Vue中有一个computed计算属性,当被代理的值发生变化时,它会被重新计算,Vuex中的派生属性getters会绑定state中的某个或某些值,通过传递参数,达到对该值过滤,修改的作用

这里我们做个简单的计算,输入字符后计算其长度并拼接一下,inputHandler中操作不变,在store中新增getters

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

Vue.use(Vuex);
const state = {
  val: ''
}

const mutations = {
  changeVal (state,_val) {
    state.val = _val
  }
}

const getters = {
  getValueLength (state) {
    return `长度:${state.val.length}`
  }
}
export default new Vuex.Store({
  state,mutations,getters
})

然后在div标签中的this.$store.state.val后面添加this.$store.getters.getValueLength

 <div>
    {{
      this.$store.state.val+this.$store.getters.getValueLength
    }}
  </div>

效果如下:


如果到这一步,你仍然感觉难度不大,那么恭喜你,Vuex的使用已经掌握了一大半了

下面,我们来说说actions,在说actions之前,我们先回顾一下mutations,mutations中注册了一些事件,在组件中通过emit对事件进行触发,达到处理异步且解耦的效果,然而官方并不推荐我们直接对store进行操作
官方对actions的说明是:
Action 类似于 mutation,不同在于1.Action 提交的是 mutation,而不是直接变更状态。2.Action 可以包含任意异步操作。

也就是说,我们要把组件中的emit操作放到actions中,而在组件中通过某些方式来触发actions中的函数间接调用emit,此时,为了让action更直观,我们添加一个清除输入框字符的方法,当点击清除按钮时清除state.val
在输入框组件中将value绑定到state上

<template>
  <input type="text" @input="inputHandler" :value="this.$store.state.val" />
</template>

<script>
export default {
  name: "inputComp",methods: {
    inputHandler(e) {
      this.$store.dispatch("actionVal",e.target.value);
    },};
</script>

<style
  scoped>
</style>

在另一个显示数据的组件中新增删除按钮并绑定删除事件,通过dispatch告知store并通过emit操作state

<template>
  <div>
    <button @click="clickHandler">清除</button>
    <span>{{ this.$store.state.val + this.$store.getters.getValueLength }}</span>
  </div>
</template>

<script>
export default {
  name: "divComp",methods: {
    clickHandler(){
      this.$store.dispatch('actionClearVal')
    }
  },};
</script>

<style
  scoped>
</style>

最后在store中新建删除的actions和mutations

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

Vue.use(Vuex);
const state = {
  val: ''
}

const mutations = {
  changeVal(state,_val) {
    state.val = _val
  },clearVal(state,_val) {
    state.val = ''
  }
}
const actions = {
  actionVal(state,_val) {
    state.commit('changeVal',_val)
  },actionClearVal(state) {
    state.commit('clearVal')
  }
}
const getters = {
  getValueLength(state) {
    return `长度:${state.val.length}`
  }
}
export default new Vuex.Store({
  state,actions,getters
})

最终效果如下:


到这里为止,Vuex的基本用法就介绍完毕了。
然而除此之外,Vuex官方还提供了辅助函数(mapState,mapMutations,mapGetters,mapActions)和Modules(store的子模块,当有许多全局状态时,我们为了避免代码臃肿,就可以将各个store分割成模块)方便我们书写
下面我们用辅助函数重新实现一下上述功能
输入框:

<template>
  <input type="text" @input="inputHandler" :value="value" />
</template>

<script>
import { mapState,mapMutations } from "vuex";
export default {
  name: "inputComp",computed: {
    ...mapState({ value: "val" }),methods: {
    ...mapMutations({ sendParams: "changeVal" }),// sendParams用来传递参数,先把sendParams注册到mutations上,输入时触发sendParams
    inputHandler(e) {
      this.sendParams(e.target.value);
    },};
</script>

<style
  scoped>
</style>

显示框:

<template>
  <div>
    <button @click="clickHandler">清除</button>
    <span>{{ value + valueLength }}</span>
  </div>
</template>

<script>
import { mapState,mapActions } from "vuex";
export default {
  name: "divComp",...mapGetters({ valueLength: "getValueLength" }),methods: {
    ...mapActions({ clickHandler: "actionClearVal" }),};
</script>

<style
  scoped>
</style>

这里需要注意的一点就是mapActions和mapMutations的传参问题,这里我使用另一个函数接收参数并且注册到store中

关于Modules的这里就不多做介绍,官方已给出了详细说明

最后,附上案例地址,有需要可以自取,感谢你看到了最后,如果这篇文章有帮助到你,请帮忙支持一下,非常感谢!
 

相关文章

https://segmentfault.com/a/1190000022018995 https://www....
ES6 (ECMAScript 6)中的模块是一个包含 JavaScript 代码的...
from https://mp.weixin.qq.com/s/-rc1lYYlsfx-wR4mQmIIQQ V...
D:\Temp&gt;npm init vite@latest vue3study --temp...
文章浏览阅读1.2k次。最近自己从零撸起的甘特图组件需要子组...
文章浏览阅读3.3k次,点赞3次,收藏16次。静默打印是什么?简...