Vuex快速入门

什么是Vuex

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。

这个状态我们可以理解为在data中的属性,需要共享给其他组件使用的部分。

也就是说,是我们需要共享的data,使用vuex进行统一集中式的管理。

什么是“状态管理模式”?

new Vue({
  // state
  data () {
    return {
      count: 0
    }
  },
  // view
  template: `
    <div>{{ count }}</div>
  `,
  // actions
  methods: {
    increment () {
      this.count++
    }
  }
})

这个状态自管理应用包含以下几个部分:

  • state,驱动应用的数据源
  • view,以声明方式将 state 映射到视图;
  • actions,响应在 view 上的用户输入导致的状态变化

以下是一个表示“单向数据流”理念的简单示意:

img

但是,当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏:

  • 多个视图依赖于同一状态
  • 来自不同视图的行为需要变更同一状态

对于问题一,传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。对于问题二,我们经常会采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码。

因此,我们为什么不把组件的共享状态抽取出来,以一个全局单例模式管理呢?在这种模式下,我们的组件树构成了一个巨大的“视图”,不管在树的哪个位置,任何组件都能获取状态或者触发行为!

通过定义和隔离状态管理中的各种概念并通过强制规则维持视图和状态间的独立性,我们的代码将会变得更结构化且易维护。

核心概念

  • state:存储状态(变量
  • getters:对数据获取之前的再次编译,可以理解为state的计算属性。我们在组件中使用 $sotre.getters.fun()
  • mutations:修改状态,并且是同步的。在组件中使用$store.commit('',params)。这个和我们组件中的自定义事件类似。
  • actions:异步操作。在组件中使用是$store.dispath('')
  • modules:store的子模块,为了开发大型项目,方便状态管理而使用的。这里我们就不解释了,用起来和上面的一样。

State

Vuex 使用单一状态树——是的,用一个对象就包含了全部的应用层级状态。至此它便作为一个“唯一数据源 (SSOT (opens new window))”而存在。这也意味着,每个应用将仅仅包含一个 store 实例。单一状态树让我们能够直接地定位任一特定的状态片段,在调试的过程中也能轻易地取得整个当前应用状态的快照。

Getter

Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。

Mutation

更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数:

Action

Action 类似于 mutation,不同在于:

  • Action 提交的是 mutation,而不是直接变更状态。
  • Action 可以包含任意异步操作。

Module

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。

为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割

具体使用

目前我们还没有引入vuex,我们需要先下载vuex,并且引入它

在保证我们处于我们项目下,在命令行输入下面命令,安装vuex

yarn add vuex

安装成功之后,我们就可以在store.js中尽情玩耍我们的vuex了!

在store.js文件中,引入vuex并且使用vuex,这里注意我的变量名是大写Vue和Vuex 

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

Vue.use(Vuex);

const state = {
  count: 12,
};

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

接下来,在main.js中引入store

import Vue from 'vue';
import App from './App.vue';
import store from './store'; // 引入store

Vue.config.productionTip = false;

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

然我我们在任意一个组件中就可以使用我们定义的count属性了。

这里我们在HelloWorld中使用一下,去除HelloWorld.vue中不用的标签

<template>
  <div class="hello">
    <h3>{{$store.state.count}}</h3>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  props: {
    msg: String,
  },
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
  margin: 40px 0 0;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
</style>

打开我们刚才运行项目的浏览器,可以看到已经使用成功了!

image

并且在vue开发工具中我们可以看到我们定义的变量count

image

到这一步,已经成功了一小半!vuex很简单吧?

回想一下,我们只需要在下载安装使用vuex,在我们定义的store.js中定义state对象,并且暴露出去

在main.js中使用我们的store.js(这里是为了防止在各个组件中引用,因为main.js中,有我们的new Vue 实例啊!)

现在我们已经使用了vuex中的state,接下来我们如何操作这个值呢? 没错!用mutations和actions。

我们继续操作store.js文件。

我们在store.js中定义mutations对象,该对象中有两个方法,mutations里面的参数,第一个默认为state,接下来的为自定义参数。

我们在mutations中定义两个方法,增加和减少,并且设置一个参数n,默认值为0,然后在Vuex.Store中使用它

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

Vue.use(Vuex);

const state = {
  count: 12,
};

const mutations = {
  mutationsAddCount(state, n = 0) {
    return (state.count += n);
  },
  mutationsReduceCount(state, n = 0) {
    return (state.count -= n);
  },
};

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

然后我们在HelloWorld.vue中,使用这个方法。

还记得我们如何在组件中使用mutations吗?就和自定义事件非常相似。

<template>
  <div class="hello">
    <h3>{{$store.state.count}}</h3>
    <div>
      <button @click="handleAddClick(10)">增加</button>
      <button @click="handleReduceClick(10)">减少</button>
    </div>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  props: {
    msg: String,
  },
  methods: {
    handleAddClick(n) {
      this.$store.commit('mutationsAddCount', n);
    },
    handleReduceClick(n) {
      this.$store.commit('mutationsReduceCount', n);
    },
  },
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
  margin: 40px 0 0;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
</style>

来浏览器看一下效果如何!

我们可以看到每当触发事件时,我们都可以在vue开发工具中看到我们触发的mutations方法,以及参数

image

接下来就是actions,actions是异步操作

创建actions对象,并且使用

这里我在两个方法中使用了两个不同的参数,一个是context,它是一个和store对象具有相同对象属性的参数。在第二个函数中,我是直接使用了这个对象的commit的方法。

凭大家喜好就行

在HelloWorld.vue中

在methods中,增加两个方法,使用dispatch来触发

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

Vue.use(Vuex);

const state = {
  count: 12,
};

const mutations = {
  mutationsAddCount(state, n = 0) {
    return (state.count += n);
  },
  mutationsReduceCount(state, n = 0) {
    return (state.count -= n);
  },
};

const actions = {
  actionsAddCount(context, n = 0) {
    console.log(context);
    return context.commit('mutationsAddCount', n);
  },
  actionsReduceCount({commit}, n = 0) {
    return commit('mutationsReduceCount', n);
  },
};

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

进入浏览器看下效果如何!

image

最后就是getters

我们一般使用getters来获取我们的state,因为它算是state的一个计算属性

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

Vue.use(Vuex);

const state = {
  count: 12,
};

const mutations = {
  mutationsAddCount(state, n = 0) {
    return (state.count += n);
  },
  mutationsReduceCount(state, n = 0) {
    return (state.count -= n);
  },
};

const actions = {
  actionsAddCount(context, n = 0) {
    console.log(context);
    return context.commit('mutationsAddCount', n);
  },
  actionsReduceCount({commit}, n = 0) {
    return commit('mutationsReduceCount', n);
  },
};

const getters = {
  getterCount(state, n = 0) {
    return (state.count += n);
  },
};

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

getters算是非常简单的了。

到这里,如果全都看懂了,vuex你已经没有压力了。

但是vuex官方给了我们一个更简单的方式来使用vuex, 也就是 {mapState, mapMutations, mapActions, mapGetters}

只要我们把上面基础的搞懂,这些都不在话下,只是方面我们书写罢了。

<template>
  <div class="hello">
    <h3>{{$store.state.count}}</h3>
    <h3>{{count}}</h3>
    <div>同步操作</div>
    <div>
      <button @click="handleAddClick(10)">增加</button>
      <button @click="handleReduceClick(10)">减少</button>
    </div>
    <div>异步操作</div>
    <div>
      <button @click="handleActionsAdd(10)">异步增加</button>
      <button @click="handleActionsReduce(10)">异步减少</button>
    </div>
  </div>
</template>

<script>
import { mapMutations, mapActions} from 'vuex';

export default {
  name: 'HelloWorld',
  props: {
    msg: String,
  },
  methods: {
    ...mapMutations({
      handleAddClick: 'mutationsAddCount',
      handleReduceClick: 'mutationsReduceCount',
    }),
    ...mapActions({
      handleActionsAdd: 'actionsAddCount',
      handleActionsReduce: 'actionsReduceCount',
    }),
    // handleAddClick(n) {
    //   this.$store.commit('mutationsAddCount', n);
    // },
    // handleReduceClick(n) {
    //   this.$store.commit('mutationsReduceCount', n);
    // },
    // handleActionsAdd(n) {
    //   this.$store.dispatch('actionsAddCount', n);
    // },
    // handleActionsReduce(n) {
    //   this.$store.dispatch('actionsReduceCount', n);
    // },
  },
  computed: {
    count() {
      return this.$store.getters.getterCount;
    },
  },
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
  margin: 40px 0 0;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
</style>
posted @ 2021-09-02 20:44  我係死肥宅  阅读(68)  评论(0编辑  收藏  举报