Vuex

一、vuex概念

1、什么是vuex

Vuex 是管理 vue 项目中的全局数据的。

作用:能够更加方便、高效地实现组件之间的数据通信。

下面的图可能会更让vuex的优点更明显的表现出来:

使用vuex的好处:

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

②:数据的流动非常清晰

③:存储在vuex中的数据都是响应式的

2、在项目中安装和配置vuex

安装和配置vuex的主要步骤大概为三步:

①:安装Vuex依赖包(注意版本号:3.6.2)

②:创建store模块

③:挂载store实例对象到new Vue()中去

第一步主要注意vuex的版本号就可以了,我直接从第二步开始代码演示,

Store/index.js

// 1.导入vue和vuex
import Vue from 'vue'
import Vuex from 'vuex'

// 2.把vuex安装为vue的插件
Vue.use(Vuex)

// 3.创建store的实例对象
const store = new Vuex.Store({
})

// 4.向外导出store的实例对象
export default store

Main.js

import Vue from 'vue'
import App from './App.vue'
import store from './store/index'
import '@/assets/css/bootstrap.css'

Vue.config.productionTip = false

new Vue({
  render: h => h(App),
  store
}).$mount('#app')

二、state

vuex中主要有四种属性,分别为state,mutation,action和getter,我们一一介绍。

1.什么是state

概念:state是用来存储全局数据的

2.state的使用步骤

①:在state中定义全局数据

②:在组件中通过this.$store.state.全局数据名称 访问全局数据

Store/index.js

const store = new Vuex.Store({
    state: {
        count: 0
    },
  })

Left.vue

<template>
  <div class="left-container">
    <h3>Left 组件</h3>
    <hr>

    <p>count 值:{{ this.$store.state.count }}</p>
      </div>
</template>

组件访问state的第二种方式

使用计算属性:

Right.vue

<template>
  <div class="right-container">
    <h3>Right 组件</h3>
    <hr>
    <p>count 值{{ count2 }}:</p>
  </div>
</template>

<script>
import {mapState} from 'vuex'

export default {
  name: 'Right',
  computed: {
    // count(){
    //   return this.$store.state.count
    // }
    // 通过...mapState(['全局数据名'])来获取全局数据
    // 获取的全局数据,可以作为当前组件的计算属性直接使用
    // ...mapState(['count'])
    
    // 可以使用对象格式的语法解决命名冲突
    // ...mapState({
    //   组件中自定义的名字:'全局的数据名'
    // })
    ...mapState({
      count2: 'count'
    }),
  },
}
</script>

<style>
</style>

三、mutation

这样传数据固然是很方便的,但是要怎样修改state中的数据呢?

1.什么是mutation?

概念:mutation是用来修改全局数据的,它是一个函数

特点:要想修改state中的全局数据,只能调用mutation方法

优点:调试工具可以正常工作和有利于后期代码维护

2.mutation的使用

①:在vuex中定义mutation方法

②:在组件中通过this.$store.commit(‘mutation函数名’),调用mutation方法

Store/index.js

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

Vue.use(Vuex)

const store = new Vuex.Store({
    state: {
        count: 0,
    },
      mutations: {
        addCount(state, n) {
            // mutation的第一个参数是state
            state.count += n
        }
    },
})
export default store

Left.vue

    <button class="btn btn-primary mr-2" @click="add">+1</button>
methods: {
    add(n) {
      // 不能在组件中直接修改state的值
      // this.$store.state.count++
      // 在组件中调用mutation函数的第一种方式
      // 语法:this.$store.commit('mutation函数名')
      this.$store.commit('addCount', n)
    },
}

但是如果我不想加1,而是加2,加3,加任何数字,要怎么做呢?

给mutation传参,方式1:

Store/index.js

 mutations: {
        addCount(state, n) {
            // mutation的第一个参数是state
            state.count += n
        }
    },

Left.vue

 methods: {
    add(n) {
      // 不能在组件中直接修改state的值
      // this.$store.state.count++
      // 在组件中调用mutation函数的第一种方式
      // 语法:this.$store.commit('mutation函数名')
      this.$store.commit('addCount', n)
    },
给mutation传参,方式2:

Rigth.vue

<button class="btn btn-warning mr-2"  @click="addCount2(-1)">-1</button>
import {mapState, mapMutations} from 'vuex'

export default {
  name: 'Right',
  computed: {
    // count(){
    //   return this.$store.state.count
    // }
    // 通过...mapState(['全局数据名'])来获取全局数据
    // 获取的全局数据,可以作为当前组件的计算属性直接使用
    // ...mapState(['count'])
    // 可以使用对象格式的语法解决命名冲突
    // ...mapState({
    //   组件中自定义的名字:'全局的数据名'
    // })
    ...mapState({
      count2: 'count'
    }),
    }
}

四、action

1.什么是action

mutation 中定义的必须是同步函数

Vuex 规定: mutation 必须是同步函数。否则,vue-devtools 将无法正常追踪 store 中数据的变化,这对大型

项目的开发调试是灾难性的!

这时候,action的出现无疑是我们开发者的福音。

action 是什么

action 是专门用来处理全局的异步操作的,它是一个函数。

2.action的使用步骤

① 在 Vuex 中,定义 action 方法

② 在组件中,通过 this.$store.dispatch(‘action函数名’) ,调用 action 方法

Store/index.js

// actions专门做异步的事情
    actions: {
        addCountAsync(ctx) {
            // mutation和action只能接收两个参数,想接收多个参数,要写成对象的形式
            // action接收的第一个参数是ctx
            // ctx是一个对象,它里面有一些store实例中的
            // 最常用的是ctx.commit(),用来调用mutation
            setTimeout(() => {
                ctx.commit('addCount', 1)
            }, 1000)
        }
    }

Left.vue

 <button class="btn btn-primary" @click="addAsync(2,2000)">2秒+2</button>
methods: {
    addAsync(n, time) {
      // 在组件中调用action的第一种方式
      // 语法:this.$store.commit('action函数名')
      this.$store.dispatch('addCountAsync', 1)
    }
  }

为了与mutation一样,提高action的通用性,action同样可以进行传参:

给action传参,方式1:

Store/index.js

// actions专门做异步的事情
    actions: {
        addCountAsync(ctx, obj) {
            // mutation和action只能接收两个参数,想接收多个参数,要写成对象的形式
            // action接收的第一个参数是ctx
            // ctx是一个对象,它里面有一些store实例中的
            // 最常用的是ctx.commit(),用来调用mutation
            setTimeout(() => {
                ctx.commit('addCount', obj.n)
            }, obj.time)
        }
    }

Left.vue

methods: {
    addAsync(n, time) {
      // 在组件中调用action的第一种方式
      // 语法:this.$store.commit('action函数名')
      this.$store.dispatch('addCountAsync', 1)
    }
  }
传递多个参数

Store/index.js

 // actions专门做异步的事情
    actions: {
        addCountAsync(ctx, obj) {
            // mutation和action只能接收两个参数,想接收多个参数,要写成对象的形式
            // action接收的第一个参数是ctx
            // ctx是一个对象,它里面有一些store实例中的
            // 最常用的是ctx.commit(),用来调用mutation
            setTimeout(() => {
                ctx.commit('addCount', obj.n)
            }, obj.time)
        }
    }

Left.vue

<button class="btn btn-primary mr-2" @click="addAsync(1,1000)">1秒+1</button>
    <button class="btn btn-primary" @click="addAsync(2,2000)">2秒+2</button>
调用action的第二种方式
<button class="btn btn-warning" @click="addCountAsync2({n:-2,time:1000})">1秒-2</button>
import {mapState, mapMutations, mapActions} from 'vuex' 
export default {
  methods: {
  name: 'Right',
...mapActions({
      addCountAsync2: 'addCountAsync'
    })
      }
}

五、Getter

1.什么是Getter

getter 是什么

getter 可以理解为是 Vuex 中的计算属性,它内部依赖于 state 中全局的数据。

当 state 中的值变化,getter 的值会自动更新。

2.Getter的使用步骤

① 在 Vuex 中,定义 getter 方法

② 在组件中,通过 this.$store.getters.getter名称,访问 getter

调用getter方式1:

Store/index.js

const store = new Vuex.Store({
  state: {
        count: 0,
        num1:0,
        num2:0
    },getters:{
        sum(state){
            return state.num1+state.num2
        }
    },
  })

Left.vue

<p>全局数据num1+num2={{ $store.getters.sum }}</p>
调用getter方式2:

Right.vue

<p>全局数据num1+num2={{ sum2 }}</p>
import {mapState, mapMutations, mapActions, mapGetters} from 'vuex'
computed: {
    // 通过...mapGetters(['全局getter名'])来获取全局计算属性
    // ...mapGetters(['sum'])
    // 使用对象格式的语法解决命名冲突
    // ...mapGetters({
    //   组件中自定义的名字:'全局的getter名'
    // })
    ...mapGetters({
      sum2: 'sum'
    })

六、module

1.module是什么

module 是什么

module 就是 Vuex 中的模块化。

根据模块化的开发思想,把有业务关联的数据和方法封装在一起。

module 的使用场景

当一个项目的页面数量很少,逻辑功能简单的情况下,是完全可以不使用 module 模块的。

但是当一个项目的页面数量很多,逻辑功能复杂的时候,所有的全局数据、方法都集中在了一起,会导致 Vuex

的结构混乱,不利于现阶段的开发和后期的维护,那么此时就需要使用模块来管理全局的数据和方法。

2.module使用

1.定义模块

一个模块就是一个 js 文件,每个模块都是彼此独立的,都可以拥有自己的 state、mutations、actions、getters 节点:

Store/task.js

export default {
    namespaced:true,
    state: {},
    mutations: {
    },
    actions: {},
    getters: {},
}

Store/count.js

export default {
    state: {
    },
    mutations: {
    },
    actions: {
    },
    getters: {
    },
}

2.注册模块

Store/index.js

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

// 导入模块
import count from './count'
import task from './task'

Vue.use(Vuex)

const store = new Vuex.Store({
    modules: {
        // 注册vuex模块
        // 模块的注册名称:导入的模块名称
        count,
        task
    }
})

export default store

3.开启命名空间

Store/task.js

export default {
    namespaced:true,
    state: {},
    mutations: {
        show(){
            console.log('调用了task模块下的show方法')
        }
    },
    actions: {},
    getters: {},
}

Store/count.js

export default {
    // 以后只要写模块,就必须要写命名空间
    namespaced: true,
    state: {
    },
    mutations: {
    },
    actions: {
    },
    getters: {
    },
}

4.代码示例

由于模块化与之前所说到的四大属性用法及其相似,只需要加上模块名称即可,因此后面不做过多介绍,直接用代码演示

Store/count.js

export default {
    // 以后只要写模块,就必须要写命名空间
    namespaced: true,
    state: {
        num: 2
    },
    mutations: {
        show() {
            console.log('调用了count模块下的show方法')
        },
        addNum(state, n) {
            state.num += n
        }
    },
    actions: {
        addNumAsync(ctx, n) {
            setTimeout(() => {
                ctx.commit('addNum', n)
            }, 1000)
        }
    },
    getters: {
        doubleNum(state) {
            return state.num * 2
        }
    },
}

Store/task.js

export default {
    namespaced:true,
    state: {},
    mutations: {
        show(){
            console.log('调用了task模块下的show方法')
        }
    },
    actions: {},
    getters: {},
}

Left.vue

<template>
  <div class="left-container">
    <h3>Left 组件</h3>
    <hr/>

    <!--    在组件中访问模块state下的num,方式1:-->
    <!--    语法:this.$store.state.模块注册名称.数据名-->
    <p>访问state模块下的num={{ $store.state.count.num }}</p>
    <button class="btn btn-primary mr-2" @click="add">+1</button>
    <button class="btn btn-primary mr-2" @click="addNumAsync">1秒后+1</button>
    <button class="btn btn-primary" @click="showBtn">show</button>

    <!--    组件中调用模块下的getter,方式1:-->
    <!--    语法:this.$store.getters['模块注册名称/getter函数名']-->
    <p>count模块下计算属性doubleNum的值:{{$store.getters['count/doubleNum']}}</p>
  </div>
</template>

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

export default {
  name: 'Left',
  computed: {
    ...mapState(['count']),
  },
  methods: {
    showBtn() {
      // 加上命名空间之后,调用模块下的mutations
      // this.$store.commit('模块的注册名称/要调用的mutations名称')
      this.$store.commit('count/show')
    },
    add() {
      // 组件中使用mutations方式一:
      // this.$store.commit('模块的注册名字/mutation的名字',参数)
      this.$store.commit('count/addNum', 1)
    },
    addNumAsync() {
      // 组件中使用actions方式一:
      // this.$store.dispatch('模块的注册名字/action的名字',参数)
      this.$store.dispatch('count/addNumAsync', 1)
    }
  }
}
</script>

<style></style>

Right.vue

<template>
  <div class="right-container">
    <h3>Right 组件</h3>
    <hr/>

    <p>访问模块state下的num={{ num2 }}</p>
    <button class="btn btn-warning mr-2" @click="addNum(-1)">-1</button>
    <button class="btn btn-warning" @click="addNumAsync(-1)">1秒后-1</button>
    <p>count模块下计算属性doubleNum的值:{{ doubleNum }}</p>
  </div>
</template>

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

export default {
  name: 'Right',
  computed: {
    // 在组件中访问state模块下的num,方式2:
    // ...mapState('模块注册名称',['要访问的数据']
    // ...mapState('count', ['num']),
    // 以对象的形式解决命名冲突
    ...mapState('count', {
      num2: 'num'
    }),
    // 组件中调用模块下的getter,方式2:
    // ...mapGetters('模块的注册名称',['要访问的getter名'])
    ...mapGetters('count', ['doubleNum'])
  },
  methods: {
    // 在组件中调用mutations方式二:
    // ...mapMutations('模块的注册名称',['mutation函数名'])
    ...mapMutations('count', ['addNum']),
    // 在组件中调用actions方式二:
    // ...mapActions('模块的注册名称',['action函数名'])
    ...mapActions('count', ['addNumAsync'])
  }
}
</script>

<style></style>

vue基础,到此结束!

Q.E.D.