1、什么是Vuex?

在使用vue作为框架的前端项目开发中,我们经常会碰到Vuex,那么Vuex到底是什么东西呢?

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

简单来说,Vuex就是一个状态管理的库,或者说是一个用来存放组件公共数据的仓库。

2、为什么要使用Vuex

要了解为什么要使用Vuex,那么我们就需要先来了解vue项目中组件之间的传参方式

2.1、组件之间的传参方法

父组件向子组件传参:

// 父组件传递参数
<template>
  <!-- 引入子组件 -->
  <son :fatherData="fatherData"></son>
</template>

<script>
import Son from "./son.vue";
export default {
  components: {
    Son,
  },
  data() {
    return {
      fatherData: "父组件的数据"
    };
  },
};
</script>

// 子组件接收参数
<template>
  <div>
    {{ fatherData }}
  </div>
</template>

<script>
export default {
  // 接收父组件传递的值
  props: {
    fatherData: {
      type: String,
      required: true,
    },
  },
};
</script>

子组件向父组件传参:

// 父组件
<template>
  <!-- 引入子组件 -->
  <div>
    <son @getData="getSonData"></son>
    <div>{{sonData}}</div>
  </div>
</template>

<script>
import Son from "./son.vue";
export default {
  components: {
    Son,
  },
  data() {
    return {
        sonData:''
    }
  },
  methods: {
    // 自定义的调用方法的参数就是子组件传递过来的数据
    getSonData(value) {
        this.sonData = value
    }
  }
};
</script>

// 子组件
<template>
  <div>
    <button @click="toFather">子向父传参</button>
  </div>
</template>

<script>
export default {
    data() {
        return {
            sonData: '子组件的数据'
        }
    },
    methods: {
        // 使用$emit来触发父组件中的自定义事件,将子组件的数据通过$emit传递给父组件
        toFather() {
            this.$emit('getData',this.sonData)
        }
    }
};
</script>

组件之间跳转的传参方法(示例由组件A跳转到组件B):

// A组件
<template>
  <button @click="toB">跳转到B页面</button>
</template>

<script>
export default {
  data() {
    return {
      aData: "A页面的数据",
    };
  },
  methods: {
    toB() {
      // 在页面跳转时,通过query传递数据
      this.$router.push({
        path: "/B",
        query: { aData: this.aData },
      });
    },
  },
};
</script>

// B组件
<template>
  <div>{{ bData }}</div>
</template>

<script>
export default {
  data() {
    return {
      bData: "",
    };
  },
  created() {
    this.bData = this.$route.query.aData;
  },
};
</script>

从上面三种组件之间传参的方式我们可以看出,组件与组件之间传递参数实现数据共享,那么组件之间需要有一定联系,要么是父子组件,要么是有跳转的关系,才能实现数据的共享。可是在完全没有联系的组件之间,他们如何实现数据的共享呢?

要想实现完全没有联系的组件之间数据共享,我们需要先思考一个问题,什么样的数据,需要同时在几个没有联系的组件中去使用呢?

2.2、 数据共享的情景

在项目开发中,我们经常会碰到一种情景,比如说一个系统,在用户登录之后,后端会将该用户的一些数据返回给前端,如用户名,token等,而这些用户数据往往不止在一个页面中使用,可能涉及到多个相互没有联系的组件,那么在这种时候,我们就需要使用到Vuex。

3、Vuex的使用

对于一个插件的使用,我们首先需要看对应的官方文档:Vuex 是什么? | Vuex (vuejs.org)。根据文档,接下来我们来一起理解Vuex。

在Vuex中有五个属性,分别为state,getters,mutations,actions,modules,下面我们将从这五个属性来学习Vuex。

3.1、state属性:

从上面的概念我们知道,Vuex是用来存放一些共享的数据,实现状态管理的一个插件,那么,我们的这些数据要放在那里呢?

在项目目录下,我们可以找到一个名为store的文件夹,文件夹中存放一个index.js的文件

Vuex的简单理解和使用_Vue

我们之后关于Vuex的一些操作,基本都会在这个文件夹下的文件中实现。

首先我们来看一下index.js文件中都有些什么

// 导入vue
import Vue from 'vue'
// 导入Vuex
import Vuex from 'vuex'
// 注册Vuex组件
Vue.use(Vuex)

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

我们可以看到,index.js中的内容基本和其他的插件的使用方式一样,我们主要看Vuex.Store中的内容,我们可以看到前面讲到的五个属性就是在Vuex.Store中,而我们的数据,就会放在state中。那么,怎么使用这个数据呢,我们使用文档中举的例子来示例。

首先,我们需要在state中加入我们要在其他组件中使用的数据

// store/index.js
export default new Vuex.Store({
    state: {
    // 添加一个变量num
    num: 0
  },
})

在store中添加了数据之后,我们需要到对应的组件中使用

组件A中使用

<template>
    <div>{{$store.state.num}}</div>
</template>

组件B中使用

<template>
    <div>{{num}}</div>
</template>

<script>
import { mapState } from 'vuex'
export default {
    computed: {
        ...mapState(['num'])
    }
}
</script>

在这里我举例了两种组件调用state中数据的方法,文档中还提到了其他的方法,可以自行查看。

到这里我们发现,我们已经实现了将数据存放在一个地方,可以供多个不相关的组件一起调用,那么好像数据共享就完成了一部分,但是我们也知道,前端的数据往往不是单纯地进行展示,而是需要进行操作,那么,对于Vuex中的数据,我们要怎么进行操作呢?

按照前端开发的思路,原则上只要我们获取到数据,好像就可以对数据进行操作了,我们既然通过Vuex的简单理解和使用_Vuex_02store.state.num进行操作就可以了,按这个思路,我们可以看看是否成功?

<template>
    <button @click="add">num加一</button>
</template>

<script>
export default {
    methods: {
        add() {
            this.$store.state.num++;
        }
    }
}
</script>

通过试验,我们发现,页面展示的数据确实达到了想要的效果,但是通过Vue开发者工具,我们发现,Vuex的state一直都没有改变

Vuex的简单理解和使用_Vue_03

很显然,我们不能直接通过$store.state.num来改变数据,那么我们到底需要怎么样改变数据呢?

3.2、mutations属性:

通过看Vuex的文档,我们可以在mutations属性介绍中发现这样一句话:更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。那么根据这句话,我们就可以知道,想要修改num,必须提交mutation,根据文档的使用方法,我们更改一下代码:

<template>
    <button @click="add">num加一</button>
</template>

<script>
export default {
    methods: {
        add() {
            this.$store.commit('addNum')
        }
    }
}
</script>
// store/index.js
export default new Vuex.Store({
  state: {
    // 添加一个变量num
    num: 0
  },
  mutations: {
    addNum(state) {
      state.num++;
    }
  },
})

结果也符合预期:

Vuex的简单理解和使用_Vue_04

那么到这里,数据的展示和操作好像都已经完成了,可是我们发现,还有三个属性都没有用到,那么这三个属性又有什么样的用处呢?

3.3、actions属性:

我们知道,在实际的项目开发中,我们会碰到很多异步操作,比较常见的就用axios请求,定时器等,那么我们对Vuex中的数据进行异步操作时是不是还是使用mutations呢?

很显然,大家看目录到这里,就知道对Vuex中的数据进行异步操作是要用到actions的,但是我们在学习mutations的时候有了解到更改 Vuex 的 store 中的状态的唯一方法是提交 mutation,那么actions是如何提交mutation的呢?

export default new Vuex.Store({
  state: {
    // 添加一个变量num
    num: 0
  },
  mutations: {
    addNum(state) {
      state.num++;
    }
  },
  actions: {
    addNum(context) {
      setTimeout(() => {
        context.commit('addNum')
      },1000)
    }
  },
})
<template>
  <div>
    <button @click="add">num加一</button>
    <button @click="delayed">延时加1</button>
  </div>
</template>

<script>
export default {
  methods: {
    add() {
      this.$store.commit("addNum");
    },
    delayed() {
      this.$store.dispatch("addNum");
    },
  },
};
</script>

我们可以看到在action中,有一个addNum方法接收了一个context参数,然后再方法中有一个定时器,定时器中通过conntext.commit调用了addNUm,看到.commit我们就会看到调用mutations时用的就是**this.Vuex的简单理解和使用_Vuex_05store呢?我们使用console.log分别输出this.$store和context

Vuex的简单理解和使用_Vue_06

在控制台中,我们可以看到,这两者有类似的属性,但是又不完全相同,但是,两者都有着同样的方法,那么就有可能是,context与store存在着某些联系,可以通过context.commit提交mutations。根据文档给出的解释是,Action函数接收了一个与store实例具有相同方法和属性的context对象,使我们可以通过context.commit来提交mutations。

3.4、modules属性:

modules的中文意思是模块,那么见名思意,我们可以大致理解为modules属性就是用来存放模块的。那么,在存放模块之前,就意味着我们需要现有模块。

在Vuex中,是允许我们将store分割成模块,每个模块拥有自己的state,mutations,action,getters甚至是modules,那么我们想象一下,为什么我们需要将store分割成模块呢?

我们知道,组件化开发,可以将根据功能划分组件,然后再讲组件整合在一起,这样既有利于我们分工,也增加了代码的复用性和便于后期的维护。而将store显然也是出于这样的考虑,我们知道,在一个大型的项目,需要进行状态管理的数据往往不是一两个变量,而是一个很复杂庞大的数据,如果我们将这些数据全部写到index.js文件中,那么整个文件就会显得冗余,而是可读性很低,那么这时候就需要我们将这些数据分割成不同的模块。

Vuex的简单理解和使用_Vuex_07

// store/index.js
export default new Vuex.Store({
  state: {
  },
  mutations: {
  },
  actions: {
  },
  modules: {
    user,
    menu,
    tab
  }
})

例如这样,我们就可以根据我们的需要,将user和menu的数据分割成一个模块,这样store/index.js这个文件就会显得很精简,而且也便于我们的更改。

3.5、getters属性:

getters这个属性主要用于对state中的数据进行一些数据处理,在某种程度上来说,比较像组件中的computed。这里的数据处理和上面的mutations的数据操作并不是同一个意思。

getters的数据数据处理是指将state处理之后在组件中展示,而mutations则是指在触发某些事件之后对数据进行对应的操作。

举个例子,我们现在state中的num是0,我们知道在点击事件提交mutations之后num会加1,但是我们现在希望在增加多一个变量的前提下,组件A展示的是1,组件B展示的是0,那么在这个时候,我们就需要用到getters对state的num进行数据处理

export default new Vuex.Store({
  state: {
    // 添加一个变量num
    num: 0
  },
  getters: {
    getAddNum(state) {
      return state.num+1
    } 
  },
  mutations: {
    addNum(state) {
      state.num++;
    }
  },
  actions: {
    addNum(context) {
      console.log("context",context)
      setTimeout(() => {
        context.commit('addNum')
      },1000)
    }
  },
})

组件A:

<template>
    <div>{{$store.getters.getAddNum}}</div>
</template>

这样,我们就可以达成我们的目的:

Vuex的简单理解和使用_Vuex_08

4、总结

Vuex的使用还有很多需要注意的地方,本文只是简单的理解Vuex的几个属性的使用方法。

需要注意的地方是:由于Vuex的版本升级,很多地方进行了修正(详情可以查看官方文档),本文的代码是基于"vuex": "^3.4.0"版本的,如果按本文的代码练习出现错误,请寻找对应的Vuex版本代码进行学习。


作者:天往
链接:https://juejin.cn/post/7139532600263049247
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。