Vue2-Vuex概念及使用场景、Vuex环境搭建、Vuex工作原理、Vuex配置项、Vuex模块化及命名空间

:山不向我走来,我便向它走去

更多Vue知识请点击——Vue.js

VUE2-Day11

    • 理解Vuex
      • 1、Vuex是什么
      • 2、什么时候使用Vuex
    • Vuex环境搭建
      • 1、安装vuex
      • 2、创建store文件
      • 3、main.js引入store
    • Vuex的工作原理
      • 1、原理图
      • 2、用案例解释工作原理
      • 3、注意点
    • Vuex配置项
      • 1、getters配置项
      • 2、mapstate与mapGetters
      • 3、mapActions与mapMutations
    • 多组件共享数据
    • Vuex模块化+命名空间(项目常用)
      • 1、模块化的概念及作用
      • 2、使用方式
      • 3、综合案例

理解Vuex

1、Vuex是什么

概念:专门在Vue中实现集中式状态(数据)管理的一个Vue插件对Vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信

  • 我们之前使用全局事件总线实现多组件共享数据,看起来挺好的问题不大,但是组件一多的话就会变得很麻烦:

Vue2-Vuex概念及使用场景、Vuex环境搭建、Vuex工作原理、Vuex配置项、Vuex模块化及命名空间_第1张图片

  • 我们现在使用Vuex实现多组件共享数据:

Vue2-Vuex概念及使用场景、Vuex环境搭建、Vuex工作原理、Vuex配置项、Vuex模块化及命名空间_第2张图片

2、什么时候使用Vuex

  1. 多个组件依赖于同一状态
  2. 来自不同组件的行为需要变更同一状态

其实就是——共享

Vuex环境搭建

1、安装vuex

安装:npm i vuex@3
注意Vue2一定要安装vuex3,如果是vue3可以直接npm i vuex安装的是vuex4,可以去package.json文件里看下vue的版本是啥,千万别输错了,不然会陷入痛苦的报错……

2、创建store文件

创建文件路径:src/store/index.js
在此文件中引入插件并使用vuex插件,使用vuex插件必须在引入store之前,如果在main.js中引入和使用vuex的话,由于js文件里所有的import语句都会提升到最开始执行,所以会报错。总结:引入store必须在Vue.use(Vuex)之后

//该文件用于创建Vuex中最为核心的store

//引入Vue
import Vue from 'vue';
//引入Vuex
import Vuex from 'vuex';//引入插件并使用插件
Vue.use(Vuex); //使用插件后就可以在vm,vc里使用store配置项

//准备actions,用于响应组件中的动作
const actions = {};

//准备mutations,用于操作数据(state)
const mutations = {};

//准备state,用于存储数据
const state = {};

//创建store
const store = new Vuex.Store({
    actions: actions,
    mutations, //简写
    state //简写
});

//导出store
export default store;

3、main.js引入store

JS执行的时候会把import提升到顶部,与摆放顺序无关,如果放在main.jsimport store from './store' 无论放到哪里都会比Vue.use(Vuex)先执行,要想把 Vue.use(Vuex) 要放到实例化之前只有放进index.js

//js文件里所有的import语句都会提升到最开始执行
// 引入Vue
import Vue from 'vue';
// 引入App
import App from './App.vue';
Vue.config.productionTip = false;

//引入store
import store from './store/index.js';

// 创建一个Vue实例
new Vue({
    el: '#app',
    store: store,  //或者直接写store
    render: h => h(App),
    beforeCreate() {
        Vue.prototype.$bus = this; //创建全局事件总线
    }
});

这样的话,vm和所有vc就都能碰到$store

Vuex的工作原理

1、原理图

Vue2-Vuex概念及使用场景、Vuex环境搭建、Vuex工作原理、Vuex配置项、Vuex模块化及命名空间_第3张图片

这个工作流程用一个通俗的例子来解释:VC就是顾客,actions就是服务员,mutations就是厨师,state就是菜的原料。顾客(VC)来到店里,先告诉(dispatch)服务员(actions)要吃啥菜,然后服务员把菜单给(commit)厨师(mutations),完了之后厨师对菜的原料(state)来个加工(mutate),最后再给(render)顾客(VC)。

2、用案例解释工作原理

使用vuex实现求和案例:

下拉框能选择要操作的数字,分别用总数对这个数字进行加减等操作。

Vue2-Vuex概念及使用场景、Vuex环境搭建、Vuex工作原理、Vuex配置项、Vuex模块化及命名空间_第4张图片

下面的操作不懂的时候可以翻到前面的原理图,结合图更容易理解哦~

1、首先数据应该放在state里面,所以把求和数据给 vuex的state对象

const state = {
    sum: 0,  //初始化数据
};

2、页面上插值语法就该用

<h1>当前求和为:{{  $store.state.sum  }}</h1>

3、在组件中的回调就可以用dispatch发给actionscommitmutations。注意如果没有业务逻辑,直接commit可以跳过actions直接给mutations(也就是上面原理图粉红色那条线)。

  • 此处加和减都没啥业务逻辑,直接跳过actions然后commit给mutations
  • 而奇数加和等一等再加存在业务逻辑,不能直接“加工”,所以要先用dispatch发给actionscommit给mutations

代码文件如下:

  • Count.vue:





  • index.js
//该文件用于创建vuex中最核心的store
// 引入Vue
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex插件
Vue.use(Vuex)
//准备actions:用于响应组件中的动作
const actions = {
  //是奇数就加
  //第一个参数是浓缩版的$store,方便你在这里调用commit把东西给mutations
  //第二个参数是传过来的数据
  oddAdd(context, value) {
    // console.log('actions中的oddadd被调用了')
    if (context.state.sum % 2) {
      context.commit('JIA', value)
    }
  },
  //等一等再加
  waitAdd(context, value) {
    // console.log('actions中的waitadd被调用了')
    setTimeout(() => {
      context.commit('JIA', value)
    }, 1000)
  },
}
//准备mutations:用于操作数据(state)
const mutations = {
  //加
  //第一个参数是state对象,第二个参数是传过来的数据  
  JIA(state, value) {
    // console.log('mutations中的JIA被调用了', state, value)
    state.sum += value
  },
  //减
  JIAN(state, value) {
    // console.log('mutations中的JIAN被调用了', state, value)
    state.sum -= value
  },
}
//准备state:用于存储数据
const state = {
  sum: 0, //初始化数据,当前的和
}

//创建store并暴露store
export default new Vuex.Store({
  actions,
  mutations,
  state,
})

3、注意点

  1. 一般来说都会把网络请求或其他业务逻辑写到actions里面
  2. 其实actions里面也可以操作数据,但是如果不在mutations里操作数据,而在actions里操作数据,vuex开发者工具会失效的
  3. 组件中也可以越过actions,即不写dispatch,直接编写commit把数据传给mutations

Vuex配置项

1、getters配置项

1、概念:当state中的数据需要经过加工后再使用时,可以使用getters加工,类似Vue中的计算属性computed。
2、使用:在store\index.js中追加getters配置,写函数,页面读的时候读的是返回值,这点其实也和计算属性很像。

......
//准备 getters ---用于将state中的数据进行加工
const getters = {
	bigSum(state){
		return state.sum * 10
	}
}

//创建并暴露store
export default new Vuex.Store({
	......
	getters
})

3、组件中读取数据:$store.getters.bigSum

<h1>当前求和放大十倍后为:{{ $store.getters.bigSum }}</h1>

4、其实state就类似于datagetters就类似computed

2、mapstate与mapGetters

我们之前要往页面上放state中的数据,还得$store.state.xxx,或者 $store.getters.xxx

<h1>当前求和为:{{  $store.state.sum  }}</h1>
<h1>当前求和放大十倍后为:{{  $store.getters.bigSum  }}</h1>
<h1>我在{{  $store.state.school  }}学习{{  $store.state.subject  }}</h1>

真的是非常麻烦啊,想简单点写,写成下面这样:

<h1>当前求和为:{{  sum  }}</h1>
<h1>当前求和放大十倍后为:{{  bigSum  }}</h1>
<h1>我在{{  school  }}学习{{  subject  }}</h1>
  • 可以用计算属性:
computed: {
    //靠程序员亲自写计算属性来实现state插值语法编码方便
    sum() {
          return this.$store.state.sum;
      },
      school() {
          return this.$store.state.school;
      },
      subject() {
          return this.$store.state.subject;
      },
      bigSum() {
         return this.$store.getters.bigSum;
     }
},

但是实际上亲自写计算属性来实现state插值语法computed这些东西复用性很差,vuex给我们提供了一个mapStatemapGetters方法:用于帮助我们把stategetters中的数据映射为计算属性

  • 用mapState和mapGetters方法

先引入这两个map方法: import {mapState,mapGetters} from 'vuex'
mapstatemapGetters用到computed里。

然后写法如下:

computed: {
    //截取mapState生成计算属性,从state读取数据。(对象写法)
    ...mapState({ sum: 'sum', school: 'school', subject: 'subject' }),
    //截取mapState生成计算属性,从state读取数据。(数组写法:生成的计算属性名和读取的属性名一样才能用)
    // ...mapState([sum, school, subject]),

    //截取mapGetters生成计算属性,从getters读取数据。(对象写法)
    ...mapGetters({ bigSum: 'bigSum' }),
    //截取mapGetters生成计算属性,从getters读取数据。(数组写法:生成的计算属性名和读取的属性名一样才能用)
    // ...mapGetters([bigSum]),
  },

注意:对象写法可以任意起名,键对应计算属性方法名,值对应state中的数据名,如果方法名和数据名一样,就可以用数组形式简写

3、mapActions与mapMutations

之前我们这个要从组件直接用commit传数据给mutations,或者用dispatch传给actions,都需要在methods里配置:

methods: {
    //程序员费老大劲写的传mutations代码
     add() {
          //如果没有业务逻辑,直接commit给mutations
          this.$store.commit('JIA', this.addnum);
      },
      subtract() {
          this.$store.commit('JIAN', this.addnum);
      },

    //程序员费老大劲写的传actions代码
      oddAdd() {
          //如果有业务逻辑,先dispatch给actions,再commit给mutations
          this.$store.dispatch('oddAdd', this.addnum);
      },
      waitAdd() {
          this.$store.dispatch('waitAdd', this.addnum);
      },
},

Vuex给我们提供了mapMutationsmapActions方法

mapMutations方法:用于帮助我们生成与mutations对话的方法,即:包含$store.commit(xxx)的函数。

mapActions方法:用于帮助我们生成与actions对话的方法,即:包含$store.dispatch(xxx)的函数。

先引入这两个map方法: import {mapMutations,mapActions} from 'vuex'
mapMutationsmapActions用到methods里。

写法如下:

methods: {
    //借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法)
    ...mapMutations({ add: 'JIA', subtract: 'JIAN' }),
    //借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(数组写法)
    // ...mapMutations(['JIA','JIAN']),

    //借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(对象写法)
    ...mapActions({ oddAdd: 'oddAdd', waitAdd: 'waitAdd' }),
    //借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(数组写法)
    // ...mapMutations(['oddAdd','waitAdd']),
  },

注意:mapActionsmapMutations使用时,其实创建的程序员费老大劲写的代码是不一样的,这两个玩意儿创建的东西是不带数据的,它创建的是这个:

 add(value) {
     this.$store.commit('JIA', value);
 },

若需要传递参数value,需要:在模板中绑定事件时传递好参数,否则参数value是事件对象。

<button @click="JIA(addnum)">+</button>
<button @click="JIAN(addnum)">-</button>
<button @click="oddAdd(addnum)">当前求和为奇数再加</button>
<button @click="waitAdd(addnum)">1秒再加</button>

注意:对象写法中,键是方法名,值是传给mutations或者actions方法名,如果方法名和传的方法名一样可以简写为数组形式

多组件共享数据

  • index.js
//该文件用于创建vuex中最核心的store
// 引入Vue
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex插件
Vue.use(Vuex)
//准备actions:用于响应组件中的动作
const actions = {
  //是奇数就加
  oddAdd(context, value) {
    // console.log('actions中的oddadd被调用了')
    if (context.state.sum % 2) {
      context.commit('JIA', value)
    }
  },
  //等一等再加
  waitAdd(context, value) {
    // console.log('actions中的waitadd被调用了')
    setTimeout(() => {
      context.commit('JIA', value)
    }, 1000)
  },
}
//准备mutations:用于操作数据(state)
const mutations = {
  //加
  JIA(state, value) {
    // console.log('mutations中的JIA被调用了', state, value)
    state.sum += value
  },
  //减
  JIAN(state, value) {
    // console.log('mutations中的JIAN被调用了', state, value)
    state.sum -= value
  },
  ADD_PERSON(state, value) {
    state.personList.unshift(value)
  },
}
//准备state:用于存储数据
const state = {
  sum: 0, //初始化数据,当前的和
  school: 'potato',
  subject: '前端',
  personList: [{ id: '001', name: '张三' }],
}
//准备getters:用于将state中的数据进行加工
const getters = {
  bigSum(state) {
    return state.sum * 10
  },
}

//创建store并暴露store
export default new Vuex.Store({
  actions,
  mutations,
  state,
  getters,
})
  • Count.vue





  • Person.vue





Vue2-Vuex概念及使用场景、Vuex环境搭建、Vuex工作原理、Vuex配置项、Vuex模块化及命名空间_第5张图片

Vuex模块化+命名空间(项目常用)

1、模块化的概念及作用

如果我们写的state,actions什么的是服务于多个种类的,比如有管加法的,有管人员的,这样放到一起很乱,所以可以把它们拆开

作用:让代码更好维护,让多种数据分类更加明确。

2、使用方式

可以都写到index.js里,也可以每个命名空间分别拆成多个js文件

const countAbout = {
namespaced:true,//开启命名空间
state:{x:1},
mutations: { ... },
actions: { ... },
getters: {...}
}
}

const personAbout = {
namespaced:true,//开启命名空间
state:{ ... },
mutations: { ... },
actions: { ... }
}

const store = new Vuex.Store({
modules: {
 countAbout,
 personAbout
}
})

(1)开启命名空间后,组件中读取state数据

//方式一:自己直接读取
this.$store.state.personAbout.list
//方式二:借助mapState读取:
...mapState('countAbout',['sum','school','subject']), 
// 前边加个参数,意思是读取countAbout 里面的 sum,school.....

(2)开启命名空间后,组件中读取getters数据

//方式一:自己直接读取
this.$store.getters['personAbout/firstPersonName'] //注意这里的写法
//方式二:借助mapGetters读取:
...mapGetters('countAbout',['bigSum'])  

(3)开启命名空间后,组件中调用dispatch
如果不写namespaced则直接写addPersonWang就可以,但是开启了命名空间,必须要加上这个名字在前边,否则会报[vuex] unknown action type: addPersonWang的错误,而且前边这个名字必须和Vuex.Store({})配置项中的名字一致。

//方式一:自己直接dispatch
this.$store.dispatch('personAbout/addPersonWang',person)
//方式二:借助mapActions:
...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})

(4)开启命名空间后,组件中调用commit

//方式一:自己直接commit
this.$store.commit('personAbout/ADD_PERSON',person)
//方式二:借助mapMutations:
...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'}),

3、综合案例

相信大家看完都有点懵了,可以直接看这个综合案例,几乎囊括了上文的所有知识点。

此案例的Count.vue和Person.vue里的计算属性和方法分别采用了两种不同的写法,Count.vue引入了mapXXX方法,Person.vue没有,可以对比一下他们的区别,你就知道mapXXX有多香。

建议大家复制下来自己运行看看,然后尝试自己凭印象再写一次。
案例目录结构如下:

Vue2-Vuex概念及使用场景、Vuex环境搭建、Vuex工作原理、Vuex配置项、Vuex模块化及命名空间_第6张图片

  • App.vue





  • main.js
// 引入Vue
import Vue from 'vue'
// 引入App
import App from './App.vue'
//引入插件
import vueResource from 'vue-resource'
//引入store
import store from './store'
// 关闭Vue的生产信息
Vue.config.productionTip = false
//使用插件
Vue.use(vueResource)
// 创建vm
new Vue({
  el: '#app',
  render: (h) => h(App),
  store,
  beforeCreate() {
    Vue.prototype.$bus = this //安装全局事件总线
  },
})
  • index.js
//该文件用于创建vuex中最核心的store
// 引入Vue
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex插件
Vue.use(Vuex)

import countAbout from './Count'
import personAbout from './Person'

//创建store并暴露store
export default new Vuex.Store({
  modules: {
    countAbout,
    personAbout,
  },
})
  • Count.js
export default {
  namespaced: true,
  state: {
    sum: 0, //初始化数据
    school: 'potato',
    subject: '前端',
  },
  getters: {
    bigSum(state) {
      return state.sum * 10
    },
  },
  mutations: {
    JIA(state, value) {
      state.sum += value
    },
    JIAN(state, value) {
      state.sum -= value
    },
  },
  actions: {
    oddAdd(context, value) {
      context.commit('JIA', value)
    },
    waitAdd(context, value) {
      setTimeout(() => {
        context.commit('JIA', value)
      }, 1000)
    },
  },
}
  • Count.vue





  • Person.js
import axios from 'axios'
import { nanoid } from 'nanoid'
export default {
  namespaced: true,
  state: {
    personList: [{ id: '001', name: '张三' }],
  },
  mutations: {
    ADD_PERSON(state, value) {
      state.personList.unshift(value)
    },
  },
  actions: {
    addPersonWu(context, value) {
      if (value.name.indexOf('吴') === 0) {
        context.commit('ADD_PERSON', value)
      } else {
        alert('添加的人不姓吴!')
      }
    },
    //发送ajax请求拿到名字
    addPersonServer(context) {
      axios.get('http://api.uixsj.cn/hitokoto/get?type=social').then(
        (response) => {
          context.commit('ADD_PERSON', { id: nanoid(), name: response.data })
        },
        (error) => {
          console.log(error.message)
        }
      )
    },
  },
  getters: {
    firstPersonName(state) {
      return state.personList[0].name
    },
  },
}
  • Person.vue





你可能感兴趣的:(Vue2+Vue3,vue.js,前端,javascript,vue,前端框架)