1 简介

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

  vue官方搭配,专属使用,有专门的调试工具。集中式管理数据状态方案 数据变化是可预测的 (响应式)

   组件之间的传值有哪些?有父子通讯,兄弟组件通讯......但是传参对于多层嵌套就显得非常繁琐,代码维护也会非常麻烦。因此vuex就是把组件共享状态抽取出来以一个全局单例模式管理,把共享的数据函数放进vuex中,任何组件都可以进行使用

   github地址:https://github.com/vuejs/vuex

2 vuex的优点

  1)数据的存取一步到位,不需要层层传递

  2)数据的流动非常清晰

  3)存储在Vuex中的数据都是响应式的

 

3 什么数据适合存储到vuex

  需要共享的数据,Vuex的作用就是:频繁、大范围的数据共享,vue官方提供的独立于组件体系之外的,管理公共数据的工具

  Vuex 可以帮助我们管理共享状态,并附带了更多的概念和框架。

  如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您最好不要使用 Vuex。一个简单的 store 模式就足够您所需了。但是,如果您需要构建一个中大型单页应用,您很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择。

4 vuex的几个概念

  1)state: 统一定义公共数据(类似于data(){return {a:1, b:2,xxxxxx}})

  2)mutations : 使用它来修改数据(类似于methods)

  3)getters: 类似于computed(计算属性,对现有的状态进行计算得到新的数据-------派生 )

  4)actions: 发起异步请求

  5)modules: 模块拆分

 

 

5 安装配置vuex

5.1 安装

  vue2安装vuex3,vue3安装vuex4

安装vuex3:npm i vuex@3
安装vuex4:npm i vuex@4

 

5.2 配置

  当我们在项目中使用vuex之后,为了方便代码维护,我们一般需要做特殊的目录调整,约定的结构如下

根组件
└── src
    ├── main.js
    └── store
        └── index.js   # vuex

 

6 基本使用

  做一个简单的示例,对一个vuex里面的一个数字进行加减操作

 

6.1 index.js

  注意事项:一般,为了便于区分,actions中的函数的名称采用小写,而mutations的函数的名称采用大写

import Vue from 'vue'
import Vuex from 'vuex'    // 引入Vuex

Vue.use(Vuex)    // 应用Vuex插件

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

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

 

6.2 在main.js创建vm时传入vuex

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

Vue.config.productionTip = false

new Vue({
    el: '#app',
    render: h => h(App),
    store,   // 配置项添加store,在这里完成配置后,在vm和vc上都可以取到$store,就可以进行vuex数据的操作了
    beforeCreate() {
        Vue.prototype.$bus = this
    }
})

 

6.3 App.vue

<template>
  <div>
    <CountNum/>
  </div>
</template>

<script>
import CountNum from "./components/CountNum.vue";

export default {
  name: "App",
  components: { CountNum },
};
</script>

 

6.4 CountNum.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>
        </select>
        <button @click="increment">+</button>
        <button @click="decrement">-</button>
        <button @click="incrementOdd">当前求和为奇数再加</button>
        <button @click="incrementWait">等一等再加</button>
    </div>
</template>

<script>
    export default {
        name:'CountNum',
        data() {
            return {
                n:1, //用户选择的数字
            }
        },
        methods: {
            increment(){
                //由于actions这里没有逻辑处理,所以跳过dispatch,直接commit调用mutations中的函数 第一个参数是函数名,第二个参数是传的参数
                this.$store.commit('JIA',this.n) 
            },
            decrement(){
                //由于actions这里没有逻辑处理,所以跳过dispatch,直接commit调用mutations中的函数,第一个参数是函数名,第二个参数是传的参数
                this.$store.commit('JIAN',this.n) 
            },
            incrementOdd(){
                //dispatch调用actions中的函数,第一个参数是函数名,第二个参数是传的参数
                this.$store.dispatch('jiaOdd',this.n)  
            },
            incrementWait(){
                //dispatch调用actions中的函数,第一个参数是函数名,第二个参数是传的参数
                this.$store.dispatch('jiaWait',this.n)  
            },
        }
    }
</script>

<style lang="css">button{margin-left: 5px;}</style>

 

6.5 效果

 

7 getters配置项

7.1 简介

  getters配置项和计算属性很像,getters和state的关系相当于计算属性和state的关系

  当state的属性需要经过比较复杂的处理再使用的时候,可以使用getters

 

7.2 使用时只需要在store中添加getter属性

......

const getters = {
    bigSum(state){
        return state.sum * 10
    }
}

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

 

7.3 示例

  在上面的例子上进一步修改

 

7.3.1 index.vue

  加上getters配置项:bigNum

import Vue from 'vue'    // 引入Vue核心库
import Vuex from 'vuex'    // 引入Vuex

Vue.use(Vuex)    // 应用Vuex插件
   
// 准备actions对象——响应组件中用户的动作
const actions = {
    addOdd(context,value){
        console.log("actions中的addOdd被调用了")
        if(context.state.sum % 2){context.commit('ADD',value)}
    },
    addWait(context,value){
        console.log("actions中的addWait被调用了")
        setTimeout(()=>{context.commit('ADD',value)},500)
    },
}
// 准备mutations对象——修改state中的数据
const mutations = {
    ADD(state,value){state.sum += value},
    SUB(state,value){state.sum -= value}
}
// 准备state对象——保存具体的数据
const state = {
    sum:0 // 当前的和
}
// 准备getters对象——用于将state中的数据进行加工
const getters = {
    bigSum(){
        return state.sum * 10
    }
}
   
//创建并暴露store
export default new Vuex.Store({
    actions,
    mutations,
    state,
    getters
})

 

7.3.2 CountNum.vue

<template>
    <div>
        <h1>当前求和为:{{ $store.state.sum }}</h1>
        <h3>当前求和的10倍为:{{ $store.getters.bigSum }}</h3>
        <select v-model.number="n">
            <option value="1">1</option>
            <option value="2">2</option>
            <option value="3">3</option>
        </select>
        <button @click="increment">+</button>
        <button @click="decrement">-</button>
        <button @click="incrementOdd">当前求和为奇数再加</button>
        <button @click="incrementWait">等一等再加</button>
    </div>
</template>

<script>
    export default {
        name:'CountNum',
        data() {
            return {
                n:1,
            }
        },
        methods: {
            increment(){this.$store.commit('ADD',this.n)},
            decrement(){this.$store.commit('SUBTRACT',this.n)},
            incrementOdd(){this.$store.dispatch('addOdd',this.n)},
            incrementWait(){this.$store.dispatch('addWait',this.n)},
        },
    }
</script>

<style>button{margin-left: 5px;}</style>

 

7.3.3 效果

 

8 四个Map方法

  它是用来简化代码的

 

8.1 mapState

  在计算属性中映射state中的属性,在组件中,我们需要$store.state.属性来获取到state的属性,在使用mapState映射后,可以直接使用映射名

computed: {
      // 借助mapState生成计算属性:sum、school、subject(对象写法一)
      ...mapState({sum:'sum',school:'school',subject:'subject'}),

      // 借助mapState生成计算属性:sum、school、subject(数组写法二)
      ...mapState(['sum','school','subject']),
},

 

8.2mapGetters

  在计算属性中映射getters中的属性,在组件中,我们需要$store.getters.属性来获取到getters的属性,在使用mapState映射后,可以直接使用映射名

computed: {
    //借助mapGetters生成计算属性:bigSum(对象写法一)
    ...mapGetters({bigSum:'bigSum'}),

    //借助mapGetters生成计算属性:bigSum(数组写法二)
    ...mapGetters(['bigSum'])
},

 

8.3 mapActions

  帮助生成和actions对话的方法

  自己写是这个样子

incrementOdd(){this.$store.dispatch('addOdd',this.n)},
  incrementWait(){this.$store.dispatch('addWait',this.n)},

  使用mapActions

methods:{
    //靠mapActions生成:incrementOdd、incrementWait(对象形式)
    ...mapActions({incrementOdd:'addOdd',incrementWait:'addWait'}) 

//靠mapActions生成:incrementOdd、incrementWait(数组形式)

   ...mapActions(['addOdd','addWait']) }

 

8.4 mapMutations

  帮助生成和mapMutations对话的方法

  自己写

increment(){this.$store.commit('ADD',this.n)},
decrement(){this.$store.commit('SUB',this.n)},

  使用mapMutations

methods:{
    //靠mapActions生成:increment、decrement(对象形式)
    ...mapMutations({increment:'ADD',decrement:'SUB'}),
    
    //靠mapMutations生成:ADD、SUBTRACT(对象形式)
    ...mapMutations(['ADD','SUB']),
}

 

注意:mapActions和mapMutations有参数传递时,需要在模板中绑定事件时传递参数,否则参数是事件对象本身

 

8.5 示例

  修改下CountNum.vue

<template>
    <div>
        <h1>当前求和为:{{ sum }}</h1>
        <h3>当前求和的10倍为:{{ bigSum }}</h3>
        <h3>我是{{ name }},我在{{ school }}学习</h3>
        <select v-model.number="n">
            <option value="1">1</option>
            <option value="2">2</option>
            <option value="3">3</option>
        </select>
        <button @click="increment(n)">+</button>
        <button @click="decrement(n)">-</button>
        <button @click="addOdd(n)">当前求和为奇数再加</button>
        <button @click="addWait(n)">等一等再加</button>
    </div>
</template>

<script>
    import {mapState, mapGetters, mapMutations, mapActions} from 'vuex'    //

    export default {
        name: 'CountNum',
        data() {
            return {
                n:1, //用户选择的数字
            }
        },
  computed: {        
            ...mapState(['sum','school','name']),
            ...mapGetters(['bigSum'])
        },
        methods: {
            ...mapMutations({increment:'ADD', decrement:'SUB'}),
            ...mapActions(['addOdd', 'addWait'])
        },
    }
</script>

<style>
    button{
        margin-left: 5px;
    }
</style>

 

9 多组件示例

1)app.vue

<template>
  <div>
    <Count/><hr/>
    <Person/>
  </div>
</template>

<script>
import Count from "./components/Count.vue";
import Person from "./components/Person.vue";

export default {
  name: "App",
  components: { Count, Person },
};
</script>

 

2)index.js

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

Vue.use(Vuex)

const actions = {
    jiaOdd(context,value){
        console.log('actions中的jiaOdd被调用了')
        if(context.state.sum % 2){
            context.commit('JIA',value)
        }
    },
    jiaWait(context,value){
        console.log('actions中的jiaWait被调用了')
        setTimeout(()=>{
            context.commit('JIA',value)
        },500)
    }
}

//准备mutations——用于操作数据(state)
const mutations = {
    JIA(state,value){
        console.log('mutations中的JIA被调用了')
        state.sum += value
    },
    JIAN(state,value){
        console.log('mutations中的JIAN被调用了')
        state.sum -= value
    },
    ADD_PERSON(state,value){
        console.log('mutations中的ADD_PERSON被调用了')
        state.personList.unshift(value)
    }
}

//准备state——用于存储数据
const state = {
    sum: 0,
    school: '尚硅谷',
    subject: '前端',
    personList: []
}

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

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

 

3) CountNum.vue

<template>
    <div>
        <h1>当前求和为:{{ sum }}</h1>
        <h3>当前求和放大10倍为:{{ bigSum }}</h3>
        <h3>我在{{ school }},学习{{ subject }}</h3>
        <h3 style="color:red">Person组件的总人数是:{{ personList.length }}</h3>
        <select v-model.number="n">
            <option value="1">1</option>
            <option value="2">2</option>
            <option value="3">3</option>
        </select>
        <button @click="increment(n)">+</button>
        <button @click="decrement(n)">-</button>
        <button @click="incrementOdd(n)">当前求和为奇数再加</button>
        <button @click="incrementWait(n)">等一等再加</button>
    </div>
</template>

<script>
    import {mapState,mapGetters,mapMutations,mapActions} from 'vuex'   //引入四个map
    export default {
        name:'CountNum',
        data() {
            return {
                n:1, //用户选择的数字
            }
        },
        computed:{
            ...mapState(['sum','school','subject','personList']),
            ...mapGetters(['bigSum'])
        },
        methods: {
            ...mapMutations({increment:'JIA',decrement:'JIAN'}),
            ...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
        }
    }
</script>

<style lang="css">button{margin-left: 5px;}</style>

 

4) PersonComp.vue

<template>
    <div>
        <h1>人员列表</h1>
        <h3 style="color:red">Count组件求和为:{{ sum }}</h3>
        <input type="text" placeholder="请输入名字" v-model="name">
        <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'  //生成id的一个插件

  
    export default {
        name:'PersonComp',
        data() {
            return {
                name:''
            }
        },
        computed:{
            personList(){return this.$store.state.personList},
            sum(){return this.$store.state.sum}
        },
        methods: {
            add(){
        if (this.name === "") return
                const personObj = {id:nanoid(),name:this.name}
                this.$store.commit('ADD_PERSON',personObj)
                this.name = ''
            }
        },
    }
</script>

 

5)效果

 

10 模块化+命名空间

10.1 简介

  对vuex进行模块化,偏于维护

 

10.2 使用

1)index.js的格式

  如下代码,将index.js的state、actions、mutations、getters分为了两个模块

const countAbout = {
  namespaced: true,    // 开启命名空间,解决命名冲突
  state: {x:1},
  mutations: { ... },
  actions: { ... },
  getters: {
    bigSum(state){ return state.sum * 10 }
  }
}

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

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

 

2)组件中读取state的数据

// 方式一:自己直接读取
this.$store.state.personAbout.list
// 方式二:借助mapState读取:
...mapState('countAbout',['sum','school','subject']),

 

3)组件中读取getters的数据

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

 

4)组件中调用actions的函数

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

 

5)组件中调用mutations的函数

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

 

10.3 示例

1)count.js

export default {
    namespaced:true,
    actions: {
        addOdd(context,value){
            console.log("actions中的addOdd被调用了")
            if(context.state.sum % 2){
                context.commit('ADD',value)
            }
        },
        addWait(context,value){
            console.log("actions中的addWait被调用了")
            setTimeout(()=>{
                context.commit('ADD',value)
            },500)
        }
    },
    mutations: {
        ADD(state,value){ state.sum += value },
        SUBTRACT(state,value){ state.sum -= value }
    },
    state: {
        sum:0,
        school:'新东方',
        subject: '前端'
    },
    getters: {
        bigSum(state){ return state.sum * 10 }
    }
}

 

2)person.js

import axios from "axios"
import { nanoid } from "nanoid"

export default{
    namespaced:true,
    actions:{
        addPersonWang(context,value){
            if(value.name.indexOf('') === 0){
                context.commit('ADD_PERSON',value)
            }else{
                alert('添加的人必须姓王!')
            }
        },
        addPersonServer(context){
            axios.get('http://api.uixsj.cn/hitokoto/get?type=social').then(
                response => {
                    context.commit('ADD_PERSON',{id:nanoid(),name:response.data})
                },
                error => { alert(error.message) }
            )
        }
    },
    mutations:{
        ADD_PERSON(state,value){
            console.log('mutations中的ADD_PERSON被调用了')
            state.personList.unshift(value)
        }
    },
    state:{
        personList:[{'name':'张三'}]
    },
    getters:{
        firstPersonName(state){ return state.personList[0].name }
    }
}

 

3)index.js

import Vue from 'vue'
import Vuex from 'vuex'
import countOptions from './count'// 引入count
import personOptions from './person'// 引入person

Vue.use(Vuex)
   
//创建并暴露store
export default new Vuex.Store({
    modules:{
        countAbout:countOptions,
        personAbout:personOptions,
    }
})

 

4)app.vue

<template>
  <div>
    <CountNum/><hr/>
    <PersonComp/>
  </div>
</template>

<script>
import CountNum from "./components/CountNum.vue";
import PersonComp from "./components/PersonComp.vue";

export default {
  name: "App",
  components: { CountNum, PersonComp },
};
</script>

 

5)CountNum.vue

<template>
 <div>
  <h1>当前求和为:{{ sum }}</h1>
  <h3>当前求和的10倍为:{{ bigSum }}</h3>
  <h3>我是{{ name }},我在{{ school }}学习</h3>
  <h3 style="color:red">Person组件的总人数是:{{ personList.length }}</h3>
  <select v-model.number="n">
   <option value="1">1</option>
   <option value="2">2</option>
   <option value="3">3</option>
  </select>
  <button @click="increment(n)">+</button>
  <button @click="decrement(n)">-</button>
  <button @click="incrementOdd(n)">当前求和为奇数再加</button>
  <button @click="incrementWait(n)">等一等再加</button>
 </div>
</template>

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

 export default {
  name:'CountNum',
  data() {
   return {
    n:1, // 用户选择的数字
   }
  },
    computed:{
   ...mapState('countAbout',['sum','school','name']),
       ...mapState('personAbout',['personList']),
   ...mapGetters('countAbout',['bigSum']),
  },
  methods: {
   ...mapMutations('countAbout',{increment:'ADD',decrement:'SUBTRACT'}),
   ...mapActions('countAbout',{incrementOdd:'addOdd',incrementWait:'addWait'})
  },
 }
</script>

<style>button{margin-left: 5px;}</style>

 

6)PersonComp.vue

<template>
  <div>
    <h1>人员列表</h1>
    <h3 style="color:red">Count组件求和为:{{ sum }}</h3>
        <h3>列表中第一个人的名字是:{{ firstPersonName }}</h3>
    <input type="text" placeholder="请输入名字" v-model="name">
    <button @click="add">添加</button>
        <button @click="addWang">添加一个姓王的人</button>
        <button @click="addPerson">随机添加一个人</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: 'PersonComp',
    data() {
      return {
        name:''
      }
    },
    computed: {
      personList(){
        return this.$store.state.personAbout.personList
      },
      sum(){
        return this.$store.state.countAbout.sum
      },
      firstPersonName(){
        return this.$store.getters['personAbout/firstPersonName']
    }
    },
    methods: {
      add(){
        const personObj = {id:nanoid(),name:this.name}
        this.$store.commit('personAbout/ADD_PERSON',personObj)
        this.name = ''
      },
      addWang(){
        const personObj = {id:nanoid(),name:this.name}
        this.$store.dispatch('personAbout/addPersonWang',personObj)
        this.name = ''   
      },
      addPerson(){
        this.$store.dispatch('personAbout/addPersonServer')
      }
    },
  }
</script>