Vue2(十三):Vuex环境搭建、Vuex工作原理、几个配置项、多组件共享数据、Vuex模块化

一、Vuex

1.理解

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

2. Github 地址:https://github.com/vuejs/vuexe

3.什么时候使用Vuex

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

2.原理图

3.Vuex环境搭建

(1)npm i 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的大小写)

//该文件用于创建Vuex中最为核心的store/
// 引入vue
import Vue from 'vue'
// 引入vuex
import Vuex from 'vuex'
Vue.use(Vuex)

// 准备actions—用于响应组件中的动作
const actions = {}
//准备mutations—用于操作数据(state)
const mutations = {}
//准备state一用于存储数据
const state = {}

const store = new Vuex.Store({
    actions,
    mutations,
    state
})
// 导出store
export default store
// 创建并暴露store,也可以这样写
// export default store = new Vuex.Store({
//     actions,
//     mutations,
//     state
// })
(3)main.js引入store

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

//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
// 引入axios
import axios from 'axios'
// 引入vue-resource
import vueResource from 'vue-resource'
//引入vuex
// import Vuex from 'vuex'
// 引入store
import store from './store/index.js'
//关闭Vue的生产提示
Vue.config.productionTip = false
Vue.prototype.$axios = axios
// 使用插件
Vue.use(vueResource)

//创建vm
new Vue({
	el:'#app',
	render: h => h(App),
	store:store,
	// store,
	beforeCreate(){
		Vue.prototype.$bus=this
	}
})

二、求和案例(Vuex)

1.先来写一个原版的Vue不使用vuex的

Count.vue
<template>
  <div>
    <h1>当前求和:{{ sum }}</h1>
    <select v-model.number="n">
      <option value="1">1</option>
      <option value="2">2</option>
      <option value="3">3</option>
      <!-- 加:变成js表达式,数值是数字不是字符串,或者加上.number='n' -->
    </select>
    <button @click="increment">+</button>
    <button @click="decrement">-</button>
    <button @click="incrementOdd">当前求和为奇数在加</button>
    <button @click="incrementWait">等一等在加</button>
  </div>
</template>

<script>
export default {
  name:'Count',
  data(){
    return{
      n:1,//用户选择的数字
      sum:0//当前的和
    }
  },
  methods:{
    increment(){
    this.sum += this.n
    },
    decrement(){
      this.sum -= this.n
    },
    incrementOdd(){
      if(this.sum % 2){
        this.sum += this.n
      }
    },
    incrementWait(){
      setTimeout(()=>{
        this.sum += this.n
      },500)
    }
  }
}
</script>
<style lang="css">
  button{
    margin-left: 5px;
  }
</style>
index.js
//该文件用于创建Vuex中最为核心的store/
// 引入vue
import Vue from 'vue'
// 引入vuex
import Vuex from 'vuex'
Vue.use(Vuex)

// 准备actions—用于响应组件中的动作
const actions = {}
//准备mutations—用于操作数据(state)
const mutations = {}
//准备state一用于存储数据
const state = {}

const store = new Vuex.Store({
    actions,
    mutations,
    state
})
// 导出store
export default store
// 创建并暴露store,也可以这样写
// export default store = new Vuex.Store({
//     actions,
//     mutations,
//     state
// })
 
App.vue
<template>
  <div>
    <Count></Count>
  </div>
</template>

<script>
import Count from './components/Count.vue'
export default {
    name:'App',
    components:{Count},
}
</script>

2.使用vuex之后

Count.vue
<template>
  <div>
    <h1>当前求和:{{ $store.state.sum }}</h1>
    <select v-model.number="n">
      <option value="1">1</option>
      <option value="2">2</option>
      <option value="3">3</option>
      <!-- 加:变成js表达式,数值是数字不是字符串,或者加上.number='n' -->
    </select>
    <button @click="increment">+</button>
    <button @click="decrement">-</button>
    <button @click="incrementOdd">当前求和为奇数在加</button>
    <button @click="incrementWait">等一等在加</button>
  </div>
</template>

<script>
export default {
  name:'Count',
  data(){
    return{
      n:1,//用户选择的数字
    }
  },
  methods:{
    increment(){
      this.$store.commit('JIA', this.n)
    },
    decrement(){
      this.$store.commit('JIAN', this.n)
    },
    incrementOdd(){
        this.$store.dispatch('jiaOdd', this.n)
    },
    incrementWait(){
      setTimeout(()=>{
        this.$store.dispatch('jiaWait', this.n)
      },500)
    }
  }
}
</script>
<style lang="css">
  button{
    margin-left: 5px;
  }
</style>
index.js
//该文件用于创建Vuex中最为核心的store/
// 引入vue
import Vue from 'vue'
// 引入vuex
import Vuex from 'vuex'
Vue.use(Vuex)

// 准备actions—用于响应组件中的动作
const actions = {
    // jia(context,value){
    //     console.log("jia被调用了");
    //     context.commit('JIA',value)
    // },
    // jian(context,value){
    //     console.log("jian被调用了");
    //     context.commit('JIAN',value)
    // },
    jiaOdd(context,value){
        console.log("jiaOdd被调用了");
        if(context.state.sum % 2){
            context.commit('JIA',value)
        }
    },
    jiaWait(context,value){
        console.log("jiaWait被调用了");
        setTimeout(()=>{
            context.commit('JIA',value)
        },500)
    },
}
//准备mutations—用于操作数据(state)
const mutations = {
    JIA(state,value){
        console.log('JIA被调用了');
        state.sum += value
    },
    JIAN(state,value){
        console.log('JIAN被调用了');
        state.sum -= value
    },
}
//准备state一用于存储数据
const state = {
    sum:0//当前的和
}

const store = new Vuex.Store({
    actions,
    mutations,
    state
})
// 导出store
export default store
// 创建并暴露store,也可以这样写
// export default store = new Vuex.Store({
//     actions,
//     mutations,
//     state
// })

3.几个注意点

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

三、几个配置项

 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 

(1)mapState

(2) mapGetters

解析

首先导入四个map方法 import {mapState,mapGetters,mapMutations,mapActions} from 'vuex'
mapstate与mapGetters用到computed里,mapMutations,mapActions用到methods里

要说之前啊,我们要往页面上放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>


真的是非常麻烦啊,想简单点写,就可以用一下子计算属性:

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;
     }
},

 诶这样就可以这么写了:

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


但是实际上computed这些东西复用性很差,vuex给我们提供了一个mapState和mapGetters方法:用于帮助我们把state和getters中的数据映射为计算属性,具体写法是下面这样滴,用到了扩展运算符,先复习一下扩展运算符:

let obj1 = {x:100, y:200};
let obj2 = {
    a:1,
    ...obj1,
    b:2
}
console.log(obj2);  //{a: 1, x: 100, y: 200, b: 2}


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

computed: {
    //靠程序员自己亲自去写计算属性:
    // sum(){return this.$store.state.sum},
    // school(){return this.$store.state.school},
    // subject(){return this.$store.state.subject},

    // 借助mapState生成计算属性,从state中读取数据(对象写法)
    // ...mapState({ he: 'sum', xuexiao: 'school', xueke: 'subject' }),
    // 借助mapState生成计算属性,从state中读取数据(数组写法)
    ...mapState(['sum','school','subject']),
    // *************************************************************************
    // bigsum(){return this.$store.getters.bigsum},
    // (对象写法)
    // ...mapGetters({bigSum:'bigSum'}),
    // (数组)
    ...mapGetters([bigSum]),
  },

3.mapActions与mapMutations

(1)mapMutations

(2)mapActions
 

解析:

methods:{
    // increment(){
    //   this.$store.commit('JIA', this.n)
    // },
    // decrement(){
    //   this.$store.commit('JIAN', this.n)
    // },

    // 对象写法
    ...mapMutations({increment:'JIA',decrement:'JIAN'}),
    // 数组写法
    ...mapMutations(['JIA','JIAN']),
    // **********************************************************
    // incrementOdd(){
    //     this.$store.dispatch('jiaOdd', this.n)
    // },
    // incrementWait(){
    //   setTimeout(()=>{
    //     this.$store.dispatch('jiaWait', this.n)
    //   },500)
    // }
    // 对象写法
    ...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'}),
    // 数组写法
    ...mapActions(['jiaOdd','jiaWait']),
  },
<button @click="increment(n)">+</button>
    <button @click="decrement(n)">-</button>
    <button @click="incrementOdd(n)">当前求和为奇数在加</button>
    <button @click="incrementWait(n)">等一等在加</button>

四、多组件共享数据

 

Person.vue

<template>
  <div>
    <h1>人员列表</h1>
    <h3 style="color:red">count组件人数为{{ sum }}</h3>
    <input type="text" placeholder="请输入名字">
    <button @click="add">添加</button>
    <ul>
        <li v-for="p in personList" :key="p.id">{p.name}</li>
    </ul>
  </div>
</template>

<script>
import {nanoid} from 'nanoid'
export default {
    name:'Person',
    data(){
        return{
            name:''
        }
    },
    computed:{
        personList(){
            return this.$store.state.personList
        },
        sum(){
            return this.$store.state.sum
        }
    },
    methods:{
        add(){
            const personObj = {id:nanoid(),name:this.name}
            // console.log(personObj);
            this.$store.commit('ADD_PERSON',personObj)
            this.name=''
        }
    }
}
    
</script>

<style>

</style>

index.js

ADD_PERSON(state,value){
        console.log('mutation中的ADD_PERSON被掉调用了');
        state.personList.unshift(value)
    }
}
//准备state一用于存储数据
const state = {
    sum:0,//当前的和
    school:'尚硅谷',
    subject:'椰果',
    personList:[
        {id:'001',name:'张三'}
    ]
}

Count.vue

<h3 style="color:red">person组件的总人数是:{{ personList.length }}</h3>

五、vuex模块化组件

(听得巨头疼。懵逼)

1.目的

让代码更好维护,让多种数据分类更明确

2.修改store.js

3.开启命名空间后,组件中读取state数据

 

4.读取getters数据 

 

5.调用dispatch

 

6.调用commit

 

真的是一脸懵,话不多说,看完整代码 

(1)index.js

//引入Vue
import Vue from 'vue';
//引入Vuex
import Vuex from 'vuex';//引入插件并使用插件
Vue.use(Vuex);

//关于计数的相关配置
import countAbout from './Count'

//关于人员的相关配置
import personAbout from './Person'


//创建并导出store
export default new Vuex.Store({
    modules: {
        countAbout: countAbout,
        personAbout  //重名简写 
    }
});

(2)count.js

export default {
    namespaced: true,
    state: {
        sum: 0,  //初始化数据
        school: '杭州',
        subject: '前端',
    },
    getters: {
        bigSum(state) {
            return state.sum * 10;
        }
    },
    actions: {
        oddAdd(context, value) {
            //第一个参数是浓缩版的$store,方便你在这里调用commit把东西给mutations
            //第二个参数是传过来的数据
            context.commit('JIA', value);
        },
        waitAdd(context, value) {
            setTimeout(() => {
                context.commit('JIA', value);
            }, 1000);
        },
    },
    mutations: {
        JIA(state, value) {
            //第一个参数是state对象,第二个参数是传过来的数据
            console.log('mutations中的JIA被调用了 ', state, value);
            state.sum += value;
        },
        JIAN(state, value) {
            state.sum -= value;
        },
    }
}

(3)count.vue

<template>
    <div>
        <h1>当前求和为:{{  sum  }}</h1>
        <h2>当前求和放大十倍后为:{{  bigSum  }}</h2>
        <h3>我在{{  school  }}学习{{  subject  }}</h3>
        <select v-model.number="addnum">
            <option value="1" checked>1</option><!-- 不写冒号就是字符串但可以v-model.number -->
            <option value="2">2</option> <!-- 不写冒号就是字符串但可以v-model.number -->
            <option value="3">3</option> <!-- 不写冒号就是字符串但可以v-model.number -->
        </select>
        <button @click="JIA(addnum)">+</button>
        <button @click="JIAN(addnum)">-</button>
        <button @click="oddAdd(addnum)">当前求和为奇数再加</button>
        <button @click="waitAdd(addnum)">等1秒再加</button>

        <h2 style="color:red">Count里边读personList</h2>
        <ul style="color:red">
            <li v-for="p in personList" :key="p.id">{{  p.name  }}</li>
        </ul>
    </div>
</template>

<script>
import { mapState, mapGetters, mapActions, mapMutations } from 'vuex';
export default {
    name: 'Count',
    data() {
        return {
            addnum: 1
        }
    },
    computed: {
        personList() {
            return this.$store.state.personAbout.personList;
        },
        ...mapState('countAbout', ['sum', 'school', 'subject']),
        ...mapGetters('countAbout', { bigSum: 'bigSum' }),
    },
    methods: {
        ...mapMutations('countAbout', ['JIA', 'JIAN']),
        ...mapActions('countAbout', { oddAdd: 'oddAdd', waitAdd: 'waitAdd' }),
    },
    mounted() {
        console.log(this.$store)
    }
};
</script>

(4)person.js

import axios from 'axios';
import { nanoid } from 'nanoid';
export default {
    namespaced: true,
    state: {
        personList: [
            { id: 1, name: 'zzy' }
        ]
    },
    getters: {
        firstPersonName(state) {
            return state.personList[0].name;
        }
    },
    actions: {
        addPersonHan(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);
                }
            )
        }
    },
    mutations: {
        ADD_PERSON(state, value) {
            state.personList.unshift(value);
        }
    }
}

(5)person.vue

<template>
    <div>
        <h2>Person里边读personList</h2>
        <input type="text" placeholder="请输入名字" v-model="name">
        <button @click="addPerson">添加</button>
        <button @click="addPersonHan">添加一个姓韩的人</button>
        <button @click="addPersonServer">随机添加一个名字</button>
        <h2>第一个人的名字:{{  firstPersonName  }}</h2>
        <ul>
            <li v-for="p in personList" :key="p.id">{{  p  }}</li>
        </ul>
        <h2 style="color:red">Person里读sum:{{  add  }}</h2>
    </div>
</template>

<script>
import { nanoid } from 'nanoid';
export default {
    name: 'Person',
    data() {
        return {
            name: ''
        }
    },
    computed: {
        personList() {
            return this.$store.state.personAbout.personList;
        },
        add() {
            return this.$store.state.countAbout.sum;
        },
        firstPersonName() {
            return this.$store.getters['personAbout/firstPersonName'];
        }
    },
    methods: {
        addPerson() {
            const personObj = { id: nanoid(), name: this.name };
            this.$store.commit('personAbout/ADD_PERSON', personObj);
            this.name = '';  //添加完了输入框置空
        },
        addPersonHan() {
            const personObj = { id: nanoid(), name: this.name };
            this.$store.dispatch('personAbout/addPersonHan', personObj);
        },
        addPersonServer() {
            this.$store.dispatch('personAbout/addPersonServer');
        }
    },
};
</script>

 vuex模块化听得脑子疼。

  • 11
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值