Vuex详细讲解

Vuex是什么?

一个程序里面的状态管理模式,它是集中式存储所有组件的状态的小仓库,并且保持我们存储的状态以一种可以预测的方式发生变化。

Vuex应用背景

假如是多级嵌套关系,你可以使用父子组件传参进行解决,虽有些麻烦,但好在可以解决;对于兄弟组件或者关系更复杂组件之间,就很难办了,虽然可以通过各种各样的办法解决,可实在很不优雅,而且等项目做大了,代码就会变得很复杂,实在令人心烦。

Vuex应用场景

如果你不需要开发大型的单页应用,此时你完全没有必要使用vuex,假如你的项目达到了中大型应用的规模,Vuex 将会成为自然而然的选择。
只有在多界面之间共享的状态,我们才将其交给vuex来管理。比如:
1.用户登录状态: 用户名, 头像, 昵称等等. 很多页面可能都会用到用户的基本信息, 像这些统一的信息, 我们就可以放在统一的地方进行管理了。
2.token: 用户登录的令牌, 某些接口必须有令牌才能访问, 那么这些几口就需要共享token。
3.商品收藏, 购物车中的物品等. 我们在各个界面都可以添加商品搜藏, 都可以加购, 这时候, 就可以将其放入到vuex里面。
放在vuex中, 不仅能够共享状态, 还能够实时响应

Vuex的使用

1.安装

进入项目,在命令行中输入安装指令:npm install vuex --save

2.新建store文件

在项目src路径下创建store文件夹,然后创建index.js文件,文件内容如下:
在这里插入图片描述

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
  state: {
    name1: '彩虹一号',
    name2: '彩虹二号',
    name3: '彩虹三号',
  }
})
export default store

3.main.js实例挂载store

引入index的store,进行实例挂载。

import Vue from 'vue'
import App from './App'
import router from './router'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import 'xe-utils'
import VXETable from 'vxe-table'
import 'vxe-table/lib/style.css'
import store from './store' 
Vue.use(ElementUI)
Vue.config.productionTip = false
Vue.use(VXETable)
new Vue({
  el: '#app',
  router,
  store,  // 挂载store
  components: { App },
  template: '<App/>'
})

4.测试Vuex数据

在App.vue中写入

<script>
export default {
  name: "App",
  data() {
    return {
    };
  },
  mounted() {
    console.log(this.$store.state.name1); // 彩虹一号
  },
  computed: {},
};
</script>

Vuex的核心部分

state => 基本数据
getters => 从基本数据派生的数据
mutations => 提交更改数据的方法,同步!
actions => 像一个装饰器,包裹mutations,使之可以异步。
modules => 模块化Vuex

1.state

上面vuex功能已经提到state,官方建议我们以上操作this.$store.state.XXX最好放在计算属性中,当然,这样可以让你的代码看起来更优雅一些,就像这样:在App.vue中写入

<script>
export default {
  name: "App",
  data() {
    return {
    };
  },
  mounted() {
    console.log(this.getMyName); // 彩虹一号
  },
  computed: {
    getMyName() {
      return this.$store.state.name1;
    },
  },
};
</script>

是不是每次都写this.$store.state.XXX让你感到厌烦,你实在不想写这个东西怎么办,当然有解决方案,就像下面这样:在App.vue中写入

<script>
import { mapState } from "vuex"; // 从vuex中导入mapState
export default {
  name: "App",
  data() {
    return {
    };
  },
  mounted() {
    console.log(this.name1); // 彩虹一号
  },
  computed: {
    ...mapState(["name1"]), // 经过解构后,自动就添加到了计算属性中,此时就可以直接像访问计算属性一样访问它
  },
};
</script>

当然,你甚至可以在解构的时候给它赋别名,取外号,就像这样:

// 赋别名的话,这里接收对象,而不是数组;相当于...mapState({ myName: state=>state.name1}),  
...mapState({ myName: 'name1' }),  

当一个组件需要获取多个状态时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以在App.vue中使用 mapState 辅助函数帮助我们生成计算属性。

<script>
import { mapState } from "vuex"; // 从vuex中导入mapState
export default {
  name: "App",
  data() {
    return {
      school: "麻省理工学院",
    };
  },
  mounted() {
    console.log(this.myName1); // 彩虹一号
    console.log(this.myName2); // 彩虹二号
    console.log(this.myName3); // 麻省理工学院彩虹三号
  },
  computed: {
    ...mapState({
      myName1: "name1", // 第一种写法,简写,等效于第二种写法
      myName2: (state) => state.name2, // 第二种写法,简单处理状态值
      // myName3: (state) => { // 错误写法,箭头函数的this指针并没有指向vue实例
      //   return this.school + state.name3;
      // }, 
      myName3: function (state) {
        return this.school + state.name3;
      },
    }),
  },
};
</script>

mapState的数组跟对象写法
数组写法:

...mapState(["name1","name2","name3"])

对象写法:

...mapState({
  myName1: "name1",
  myName2: (state) => state.name2, 
  myName3: function (state) {
    return this.school + state.name3;
  },
}),

2.Getter

getter是 store 的计算属性,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
首先,在store对象中增加getters属性

import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);

const store = new Vuex.Store({
  state: {
    name1: "彩虹一号",
    name2: "彩虹二号",
    name3: "彩虹三号",
  },
  getters: {
    getWords1(state) {
      return `我的名字叫${state.name1}`;
    },
    getWords2(state) {
      return `我的名字叫${state.name2}`;
    },
    getWords3(state) {
      return `我的名字叫${state.name3}`;
    },
  }
});

export default store;

接受state作为第一个参数,通过this.$store.getters调用。

  mounted() {
    console.log(this.$store.getters.getWords1); // 我的名字叫彩虹一号
  },

也可以使用辅助函数mapgetters将 store 中的 getter 映射到局部计算属性,在App.vue中先引入mapGetters 。

<script>
import { mapState, mapGetters } from "vuex";
export default {
  name: "App",
  data() {
    return {
      school: "麻省理工学院",
    };
  },
  mounted() {
    console.log(this.getWords1, this.getWords2, this.getWords3); // 我的名字叫彩虹一号,我的名字叫彩虹二号,我的名字叫彩虹三号
    console.log(this.getWs1, this.getWs2, this.getWs3); // 我的名字叫彩虹一号,我的名字叫彩虹二号,我的名字叫彩虹三号
  },
  computed: {
    ...mapGetters(["getWords1", "getWords2", "getWords3"]), // 数组写法
    ...mapGetters({ // 重命名写法
      getWs1: "getWords1",
      getWs2: "getWords2",
      getWs3: "getWords2",
    }),
  },
};
</script>

3.Mutation

mutation类似于事件,可以改变state的状态,必须同步操作,在组件中改变状态时通过this.$store.commit(‘事件名’,传递的参数)调用mutation里面的事件。Vuex中不能直接对store的状态进行赋值修改。
在store中定义mutation:

import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);

const store = new Vuex.Store({
  state: {
    name1: "彩虹一号",
    name2: "彩虹二号",
    name3: "彩虹三号",
  },
  getters: {
    getWords1(state) {
      return `我的名字叫${state.name1}`;
    },
    getWords2(state) {
      return `我的名字叫${state.name2}`;
    },
    getWords3(state) {
      return `我的名字叫${state.name3}`;
    }
  },
  mutations: {
    changeName(state) {
      state.name1 = '银河一号';
    }
  }
});

export default store;

我们不能直接this. s t o r e . m u t a t i o n s . c h a n g e N a m e ( ) 来 调 用 , V u e x 规 定 必 须 使 用 t h i s . store.mutations.changeName() 来调用,Vuex 规定必须使用 this. store.mutations.changeName()Vuex使this.store.commit 来触发对应 type 的方法:在App.vue中commit改变state中name1的值。

mounted() {
  console.log(`旧值:${this.$store.state.name1}`); // 彩虹一号
  this.$store.commit('changeName');
  console.log(`新值:${this.$store.state.name1}`); // 银河一号
},

以上是简单实现mutations的方法,是没有传参的,如果我们想传不固定的参数怎么办?mutations中定义changeNameAgain函数:

mutations: {
  changeName(state) {
    state.name1 = "银河一号";
  },
  changeNameAgain(state, name) {
    state.name1 = name;
  }
 }

App.vue组件内commit修改:

mounted() {
  this.$store.commit('changeNameAgain','北斗一号');
  console.log(`新值:${this.$store.state.name1}`); // 北斗一号
},

Mutations里面的函数必须是同步操作,不能包含异步操作!
就像最开始的mapState和mapGetters一样,我们在组件中可以使用mapMutations以代替this.$store.commit(‘XXX’),是不是很方便呢?
记住在App.vue引入mapMutations

<script>
import { mapState, mapGetters, mapMutations } from "vuex";
export default {
  name: "App",
  mounted() {
    // this.changeNameAgain('银河一号')` 映射为`this.$store.commit('changeNameAgain','银河')
    this.changeNameAgain('银河一号');
    console.log(this.$store.state.name1) // 银河一号
  },
  methods: {
    ...mapMutations(["changeNameAgain"]),
  },
  computed: {},
};
</script>

同理,如下赋别名写法也是可以的:

mounted() {
  this.changeName1('银河一号');
  console.log(this.$store.state.name1) // 银河一号
},
methods:{
  ...mapMutations({ changeName1: 'changeNameAgain' }),   // 赋别名的话,这里接收对象,而不是数组
}

Mutation常量类型 – 概念
在mutation中, 我们定义了很多事件类型(也就是其中的方法名称),当我们的项目增大时, Vuex管理的状态越来越多,需要更新状态的情况越来越多,那么意味着Mutation中的方 法越来越多。
方法过多, 使用者需要花费大量的经历去记住这些方法, 甚至是多个文件间来回切换, 查看方法名称, 甚至如果不 是复制的时候, 可能还会出现写错的情况。mutation函数名 和 $store.commit(‘函数名’) 都改变,这样保证 错误率降低。
在store文件夹中新建js文件,名字叫做mutations-types,用来存放将要在mutations中定义的函数名。
在这里插入图片描述
mutations-types存放将要在mutations中定义的函数名如下:

export const CHANGE_NAME_ONE = 'CHANGE_NAME_ONE'
export const CHANGE_NAME_TWO = 'CHANGE_NAME_TWO'
export const CHANGE_NAME_THREE = 'CHANGE_NAME_THREE'

之后进入index.js文件,首先导入mutations-types.js文件,在mutations中定义函数。注意使用常量定义函数时需要使用该格式:[函数名] (参数){函数具体内容}

import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);

import * as types from "./mutations-types";
// import { CHANGE_NAME_ONE, CHANGE_NAME_TWO, CHANGE_NAME_THREE } from "./mutations-types"; // 也可以按需引入
const store = new Vuex.Store({
  state: {
    name1: "彩虹一号",
    name2: "彩虹二号",
    name3: "彩虹三号"
  },
  mutations: {
    [types.CHANGE_NAME_ONE](state) {
      state.name1 = "银河一号";
    },
    // [CHANGE_NAME_ONE](state) { // 对应按需引入写法的函数
    //   state.name1 = "银河一号";
    // },
    [types.CHANGE_NAME_TWO](state, payload) {
      state.name2 = payload;
    },
    [types.CHANGE_NAME_THREE](state, payload) {
      state.name3 = payload.name;
    }
  }
});

export default store;

App.vue中修改:可以看到打印的this.name1, this.name2, this.name3值为:银河一号 银河二号 银河三号

<script>
import { mapState, mapGetters, mapMutations } from "vuex";
export default {
  name: "App",
  data() {
    return {};
  },
  mounted() {
    this.CHANGE_NAME_ONE();
    this.CHANGE_NAME_TWO("银河二号");
    this.CHANGE_NAME_THREE({ name: "银河三号" });
    console.log(this.name1, this.name2, this.name3); // 银河一号 银河二号 银河三号
  },
  methods: {
    ...mapMutations([
      "CHANGE_NAME_ONE",
      "CHANGE_NAME_TWO",
      "CHANGE_NAME_THREE",
    ]),
  },
  computed: {
    ...mapState(["name1", "name2", "name3"]),
  },
};
</script>

不引用辅助函数mapState, mapMutations的写法如下(注意引入mutations-types):

<script>
import * as types from "./store/mutations-types";
export default {
  name: "App",
  data() {
    return {};
  },
  mounted() {
    console.log(types)
    this.$store.commit(types.CHANGE_NAME_ONE);
    this.$store.commit(types.CHANGE_NAME_TWO, "银河二号");
    this.$store.commit(types.CHANGE_NAME_THREE, { name: "银河三号" });
    console.log(
      this.$store.state.name1,
      this.$store.state.name2,
      this.$store.state.name3
    ); // 银河一号 银河二号 银河三号
  },
};
</script>

4.Actions

Actions存在的意义是假设你在修改state的时候有异步操作,vuex作者不希望你将异步操作放在Mutations中,所以就给你设置了一个区域,让你放异步操作,这就是Actions。
我们再看一下这张图,action的声明周期就是上面那一块,它在组件里被dispatch到,然后进入action,执行异步操作,异步操作(promise)总有一个结果的,出结果后再决定提不提交mutation

在这里插入图片描述
Action 类似于 mutation,不同在于Action 提交的是 Mutation,不是直接变更状态。Action 可以包含任意异步操作。
在store的index.js文件定义Actions的方法:

import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);

import * as types from "./mutations-types";
const store = new Vuex.Store({
  state: {
    name1: "彩虹一号"
  },
  mutations: {
    [types.CHANGE_NAME_ONE](state, payload) {
      state.name1 = payload.name;
    }
  },
  actions: {
    changeNameOne(content) {
      // 默认第一个参数为content
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          content.commit(types.CHANGE_NAME_ONE, { name: "银河一号" });
          resolve(content.state.name1);
        }, 1000);
      });
    }
  }
});

export default store;

在App.vue通过提交Actions方法修改state.name1的值。

<script>
import * as types from "./store/mutations-types";
export default {
  name: "App",
  data() {
    return {};
  },
  async mounted() {
    console.log(this.$store.state.name1); // 彩虹一号
    await this.$store.dispatch("changeNameOne");
    console.log(this.$store.state.name1); // 银河一号
    this.$store.dispatch("changeNameOne").then((res) => {
      console.log(res); // 银河一号
    });
  },
};
</script>

当然也可以通过传参的方式修改,此时修改下actions的changeNameOne方法,改成传参形式,content后面为传过来的参数。

actions: {
  changeNameOne(content, payload) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        content.commit(types.CHANGE_NAME_ONE, payload);
        resolve(content.state.name1);
      }, 1000);
    });
  }
}

此时App.vue传参修改state.name1的值。

<script>
import * as types from "./store/mutations-types";
export default {
  name: "App",
  data() {
    return {};
  },
  async mounted() {
    console.log(this.$store.state.name1); // 彩虹一号
    let awaitRes= await this.$store.dispatch("changeNameOne", {name: '银河一号'});
    console.log(awaitRes); // 银河一号
    console.log(this.$store.state.name1); // 银河一号
  },
};
</script>

上面的这句代码

let awaitRes= await this.$store.dispatch("changeNameOne", {name: '银河一号'});

其实已经执行了,直接这样写:this.$store.dispatch(“changeNameOne”, {name: ‘银河一号’})就够了,程序员偶尔还是要皮一下的。
让我们看看Actions中的content,Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters。

// context可以理解为state的父级,上一级,包含着state中的所有属性
context: {
  state, // 等同于store.$state,若在模块中则为局部状态
  rootState, //等同于store.$state,只存在模块中
  commit, //等同于store.$commit
  dispatch, //等同于store.$dispatch
  getters, //等同于store.$getters
  //等等
},

在store/index.js中的actions里面,方法的形参可以直接将commit解构出来,这样可以方便后续操作,同理state 也可以结构。

const store = new Vuex.Store({
  state: {
    name1: "彩虹一号"
  },
  mutations: {
    [types.CHANGE_NAME_ONE](state, payload) {
      state.name1 = payload.name;
    }
  },
  actions: {
    changeNameOne({ commit, state }, payload) { // 形参解构入参{ commit, state }
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          commit(types.CHANGE_NAME_ONE, payload);
          resolve(state.name1);
        }, 1000);
      });
    }
  }
});

此时App.vue执行,一样的结果。

<script>
import * as types from "./store/mutations-types";
export default {
  name: "App",
  data() {
    return {};
  },
  async mounted() {
    console.log(this.$store.state.name1); // 彩虹一号
    let awaitRes= await this.$store.dispatch("changeNameOne", {name: '银河一号'});
    console.log(awaitRes); // 银河一号
    console.log(this.$store.state.name1); // 银河一号
  },
};
</script>

我们也可以在actions里面触发内部actions方法:

import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);

import * as types from "./mutations-types";
const store = new Vuex.Store({
  state: {
    name1: "彩虹一号"
  },
  mutations: {
    [types.CHANGE_NAME_ONE](state, payload) {
      state.name1 = payload.name;
    }
  },
  actions: {
    changeNameOne({ commit, state }, payload) {
      return new Promise((resolve, reject) => {
        commit(types.CHANGE_NAME_ONE, payload);
        resolve(state.name1);
      });
    },
    changeNameOneAgain({ dispatch, state }, payload) {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          dispatch("changeNameOne", payload); // dispatch触发actions内部的changeNameOne方法
          resolve(state.name1);
        }, 1000);
      });
    }
  }
});

export default store;

App.vue执行,一秒后执行结果state.name1值为北斗一号

<script>
import * as types from "./store/mutations-types";
export default {
  name: "App",
  data() {
    return {};
  },
  async mounted() {
    console.log(this.$store.state.name1); // 彩虹一号
    await this.$store.dispatch("changeNameOneAgain", {name: '北斗一号'});
    console.log(this.$store.state.name1); // 北斗一号
  },
};
</script>

你也可以采用mapActions的方式,把相关的actions解构到methods中。

<script>
import { mapActions } from "vuex";
export default {
  name: "App",
  data() {
    return {};
  },
  async mounted() {
    console.log(this.$store.state.name1); // 彩虹一号
    await this.changeNameOneAgain({ name: "北斗一号" });
    console.log(this.$store.state.name1); // 北斗一号
  },
  methods: {
    ...mapActions(["changeNameOneAgain"]),
  },
};
</script>

5.module

由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割。
下面的链接挺详细的,可以参考下:
module的使用

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Vue中使用Vuex进行状态管理是非常常见的,它可以帮助我们更好地组织和管理应用程序的状态。下面是一个简单的例子,演示如何在Vue中使用Vuex进行传值。 1. 安装Vuex 首先,我们需要安装Vuex。可以使用npm或yarn来安装: ``` npm install vuex --save ``` 或者 ``` yarn add vuex ``` 2. 创建store 在Vue中使用Vuex需要先创建一个store。在store中,我们可以定义和管理应用程序的所有状态。 ``` import Vue from 'vue'; import Vuex from 'vuex'; Vue.use(Vuex); const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment(state) { state.count++ } } }); export default store; ``` 在这个例子中,我们定义了一个名为“count”的状态和一个名为“increment”的mutation,当我们调用“increment”mutation时,它会增加“count”状态的值。 3. 在Vue组件中使用store 现在我们已经创建了一个store,接下来让我们在Vue组件中使用它。我们需要使用Vuex提供的“mapState”和“mapMutations”辅助函数来访问store中的状态和mutations。 ``` <template> <div> <h1>{{ count }}</h1> <button @click="increment">Increment</button> </div> </template> <script> import { mapState, mapMutations } from 'vuex'; export default { computed: { ...mapState(['count']) }, methods: { ...mapMutations(['increment']) } } </script> ``` 在这个例子中,我们使用了“mapState”辅助函数来将“count”状态映射到组件的计算属性中,并使用“mapMutations”辅助函数将“increment”mutation映射到组件的方法中。这样,在组件中就可以直接使用“count”状态和“increment”mutation了。 以上就是使用Vuex进行状态管理的基本过程。在实际开发中,我们通常需要定义更多的状态和mutations,并使用actions和getters等高级特性来管理更复杂的状态。但基本的使用方法和原理都是相似的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值