vue全家桶之vuex状态管理的入门速成秘籍

作为vue全家桶中重要一员,在vue构建项目中我们会经常用到vuex,

关于VueX

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式**。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。
VueX是适用于在Vue项目开发时使用的状态管理工具。试想一下,如果在一个项目开发中频繁的使用组件传参的方式来同步data中的值,一旦项目变得很庞大,管理和维护这些值将是相当棘手的工作。为此,Vue为这些被多个组件频繁使用的值提供了一个统一管理的工具——VueX。在具有VueX的Vue项目中,我们只需要把这些值定义在VueX中,即可在整个Vue项目的组件中使用。

为什么要使用vuex
例如:你有几个数据,几个操作,在多个组件上都需要使用,如果每个组件都去调用都是写,就会很麻烦,代码繁琐又长。使用起来很容易乱,这个时候如果我们用vuex统一管理,管理起来会很方便,修改数据也比较方便,比较简明。

什么情况下我应该使用 Vuex?
Vuex 可以帮助我们管理共享状态,并附带了更多的概念和框架。这需要对短期和长期效益进行权衡。

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

没有vuex组件之间共享数据的方式

父向子传值:v-bind 属性绑定

子向父传值:v-on 事件绑定

兄弟组件之间数据共享:EventBus

  • $on 接收数据的那个组件
  • $emit 发送数据的那个组件

在这里插入图片描述

使用 Vuex 统一管理状态的好处

Vuex 是实现组件全局状态(数据)管理的一种机制,可以方便的实现组件之间数据的共享。

1、能够在 vuex 中集中管理共享的数据,易于开发和后期维护

2、能够高效地实现组件之间的数据共享,提高开发效率

3、储存在vuex中的数据都是响应式的,能够实时保持数据与页面的同步

在这里插入图片描述

vuex中存什么

多个组件共享状态,才存储在vuex中

某个组件中的私有数据,依旧存储在data中

例如:

  • 登陆的用户名需要在首页, 个人中心, 结算页面使用, 用户名存在vuex中
  • 文章详情数据, 只有在文章详情页查看, 在自身data中声明

Vuex 的基本使用

1.安装vuex

npm i vuex -S

2.store/index.js - 创建定义导出store对象

注册到Vue中,函数也是一个对象,也可以点属性

new一个Vuex的Store的数据仓库

// 1. 引入vuex
import Vuex from 'vuex'
// 2. Vue.use注册使用
Vue.use(Vuex)
// 3. 实例化store对象 这个store对我们的状态进行管理
const store = new Vuex.Store({
    
    
//4.
})
// 5. 导出store对象
export default store

3.将store挂载到当前项目的Vue实例当中去

到main.js中导入store

import store from './store'

Vue.config.productionTip = false

new Vue({
    
    
  // 注入到vue实例中
  store, //store:store将我们创建的Vuex实例挂载到这个vue实例中
  render: h => h(App),
}).$mount('#app')

4.在组件中使用Vuex

例如在App.vue中,我们要将state中定义的name拿来在h1标签中显示

<template>
    <div id='app'>
        name:
        <h1>{
    
    {
    
     $store.state.name }}</h1>
    </div>
</template>

数据源的使用:

只能在组件内(.APP结尾的文件是组件)才可以直接使用和映射(辅助函数)

在.js后缀结尾的文件中,需要导入使用

import store from '@/store' //引入
store.state.token        //使用

state数据源

State 提供唯一的公共数据源,所有共享的数据都要统一放到 Store 的 State 中进行存储。

定义state

/*语法格式
const store = new Vuex.Store({
    state: {
        变量名: 初始值
    }
})	
*/
//具体代码:
const store = new Vuex.Store({
    
    
    state: {
    
    
        count: 100 // 库存
    }
})

使用state的2种方式:

1、组件访问 State 中的数据的第一种方式:组件内 - 直接使用

// this.$store.state.全局数据名称
this.$store.state.count

2、组件访问 State 中的数据的第二种方式:组件内 - 映射使用 (推荐)

// 1. 拿到mapState辅助函数
import {
    
     mapState } from 'vuex'
export default {
    
    
    computed: {
    
    
        // 2. 把state里变量映射到计算属性中
        ...mapState(['count']) //count就是state里的变量名
    }
}

通过刚才导入的mapState函数,将当前组件需要的全局数据。映射为当前组件的computed计算属性:

<template>
  <div>
    <h3>方法一:当前最新的count值:{
    
    {
    
    $store.state.count}}</h3>
    <h3>方法二-03:当前最新的count值:{
    
    {
    
    count}}</h3>
    <button>+1</button>
  </div>
</template>

<script>
import {
    
     mapState } from 'vuex' //方法二-01

export default {
    
    
  computed: {
    
     //方法二-02
    ...mapState(['count'])
  }
}
</script>

注意

state是响应式的, 只要state值变化, 页面上使用的地方会自动更新同步

小结

  1. state作用?

    定义全局状态数据源

  2. state如何定义?

    在store内, state: {变量名: 初始值}

  3. state的值如何用到具体vue组件内?

    • 直接使用 this.$store.state.变量名
    • 映射使用 …mapState([‘state的变量名’])

mutations-同步修改

mutations类似数据管家, 操作state里的数据

在store/index.js定义mutations

语法:

/*
const store  = new Vuex.Store({
	mutations: {
		函数名 (state, 可选值) {
			// 同步修改state值代码
		}
	}
})
*/

参考代码

const store  = new Vuex.Store({
    
    
    state: {
    
    
        count: 100 // 库存
    },
	mutations: {
    
    
		addCount (state, value) {
    
     // 负责增加库存的管家
			state.count += value
		},
        subCount (state, value) {
    
     // 负责减少库存的管家
            state.count -= value
        },
        setCount (state, value) {
    
     // 负责直接修改库存的管家
            state.count = value;
        }
	}
})

注意

  1. mutations是唯一能修改state的地方, 确保调试工具可以追踪变化
  2. mutations函数内, 只能写同步代码, 调试工具可追踪变化过程
    • 因为调试工具要立刻产生一次记录, 所以必须是同步的

小结

  1. mutations里函数作用?

    • 负责修改state里的数据
  2. mutations只能写什么样的代码?

    • 同步流程的代码

使用mutations的2种方式

  • 方式1: 组件内 - 直接使用d

    语法:

    this.$store.commit("mutations里的函数名", 具体值)
    
  • 方式2: 组件内 - 映射使用

    语法:

    // 1. 拿到mapMutations辅助函数
    import {
          
           mapMutations } from 'vuex'
    export default {
          
          
        methods: {
          
          
            // 2. 把mutations里方法映射到原地
            ...mapMutations(['mutations里的函数名'])
        }
    }
    

AddItem直接用

  • 点击事件绑定
  • 提交mutations传入值
<button @click="addFn">库存+1</button>

<script>
export default {
  methods: {
    addFn(){
      this.$store.commit('addCount', 1)
    }
  }
}
</script>

App.vue直接用

  • 触发计算属性的set方法
  • 提交mutations传入值
<span>库存总数: </span>
<input type="text" v-model="count">

<script>
export default {
  computed: {
    count: {
      set(val){
        this.$store.commit('setCount', val) // 把表单值提交给store下的mutations
      },
      get(){
        return this.$store.state.count
      }
    }
  }
}
</script>

SubItem映射用

  • 点击事件
  • 映射mutations的方法
  • 调用mutations方法传值
<button @click="subFn">库存-1</button>

<script>
// 需求2: 映射mutations到方法里
// 1. 拿到辅助函数 mapMutations
// 2. 在methods内, ...mapMutations(['mutations函数名'])
// 3. 当普通方法使用

import { mapMutations } from 'vuex'
export default {
  methods: {
    ...mapMutations(['subCount']),
    subFn(){
      this.subCount(1)
    }
  }
}
</script>

注意

mutations函数上, 只能接收一个参数值, 如果传对个, 请传一个对象

小结

  1. mutations有哪2种使用方式?

    直接使用 this.$store.commit()

    映射使用 mapMutations把方法映射到组件内直接调用

  2. state, mutations, 视图组件, 3个关系是什么?

在这里插入图片描述

actions-异步修改

Action 类似于 mutation,不同在于:

  • Action 提交的是 mutation,而不是直接变更状态。
  • Action 可以包含任意异步操作。

如果通过异步操作变更数据,必须通过Action,而不能使用Mutation,但是在Action中还是要通过触发Mutation的方式去间接变更数据。

定义actions

在store/index.js定义actions

语法:

/*
const store = new Vuex.Store({
	actions: {
		函数名 (store, 可选值) {
			// 异步代码, 把结果commit给mutations给state赋值
		}
	}
})
*/

具体代码:

const store  = new Vuex.Store({
    
    
    // ...省略state和mutations此处
    actions: {
    
    
        asyncAddCount(store, num){
    
    
            setTimeout(() => {
    
     // 1秒后, 异步提交给add的mutations
                store.commit('addCount', num)
            }, 1000)
        },
        asyncSubCount(store, num) {
    
    
            setTimeout(() => {
    
     // 1秒后, 异步提交给sub的mutations
                store.commit('subCount', num)
            }, 1000)
        }
    }
})

小结

  1. actions和mutations区别?

    mutations里同步修改state

    actions里放入异步操作

  2. actions是否能操作state?

    不建议, 要commit给mutations(为调试工具可追踪)

  3. actions和mutations里函数, 第一个形参分别是什么?

    mutations的是state

    actions的是store

使用actions的2种方式

  • 方式1: 组件内 - 直接使用

    语法:

    this.$store.dispatch('actions函数名', 具体值)
    
  • 方式2: 组件内 - 映射使用

    语法:

    // 1. 拿到mapActions辅助函数
    import {
          
           mapActions } from 'vuex'
    export default {
          
          
        methods: {
          
          
            // 2. 把actions里方法映射到原地
            ...mapActions(['actions里的函数名'])
        }
    }
    

案例代码:

AddItem直接用

  • 点击事件
  • dispatch触发action
<button @click="asyncAddFn">延迟1秒, 库存+5</button>

<script>
export default {
  methods: {
    asyncAddFn(){
      this.$store.dispatch('asyncAddCount', 5)
    }
  }
}
</script>

SubItem映射用

  • 点击事件
  • 映射actions的方法
  • 调用actions的方法传值
<button @click="asyncSubFn">延迟1秒, 库存-5</button>

<script>
// 需求3: 映射actions到方法里
// 1. 拿到辅助函数 mapActions
// 2. 在methods内, ...mapActions(['actions函数名'])
// 3. 当普通方法使用

import { mapActions } from 'vuex'
export default {
  methods: {
    ...mapActions(['asyncSubCount']),
    asyncSubFn(){
      this.asyncSubCount(5)
    }
  }
}
</script>

小结

  1. actions使用方式?

    方式1: this.$store.dispatch(‘actions方法名字’, 值)

    方式2: …mapActions([‘actions里的方法名’]) 映射到原地使用

  2. 视图组件, state, mutations, actions的关系是?

在这里插入图片描述

getters-计算属性

getters概念

vuex的计算属性,

Getter 用于对 Store 中的数据进行加工处理形成的新的数据。

  • 1、Getter类似vue的计算属性
  • 2、Store中数据的变化,Getter的数据也会发生变化

定义getters

在store/index.js定义getters

语法:

// 定义 Getter
const store = new Vuex.Store({
    
    
    state: {
    
    
        count: 0
    },
    getters: {
    
    
        getSum: state => {
    
    
            return `当前的新数据是【${
      
      state}`
        }
    }
})

使用getters的2种方式

  • 方式1: 组件内 - 直接使用

    语法:

    //this.$store.getters.计算属性名
    this.$store.getters.getSum
    
  • 方式2: 组件内 - 映射使用

    语法:

    // 1. 拿到mapGetters辅助函数
    import {
          
           mapGetters } from 'vuex'
    export default {
          
          
    	computed: {
          
          
            // 2. 把getters里属性映射到原地
          ...mapGetters(['getSum'])
        }   
    }
    

modules-分模块

当项目庞大,状态非常多时,可以采用模块化管理模式。Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割。

下面是不分模块和 分模块的对比:

在这里插入图片描述

创建modules模块对象

  • 新建store/modules/user.js
  • 新建store/modules/cart.js

语法: 对象里包含5个核心概念, 只有state变成函数形式

user.js - 用户模块对象

// 用户模块对象
const userModule = {
    
    
    state(){
    
    
        return {
    
    
            name: "",
            age: 0,
            sex: ''
        }
    },
    mutations: {
    
    },
    actions: {
    
    },
    getters: {
    
    }
}
export default userModule

cart.js - 购物车模块对象

// 购物车模块对象
import axios from 'axios'
const cartModule = {
    
    
    state() {
    
    
        return {
    
    
            goodsList: []
        }
    },
    mutations: {
    
    
        setGoodsList(state, newList) {
    
    
            state.goodsList = newList
        }
    },
    actions: {
    
    
        async asyncGetGoodsList(store) {
    
    
            const url = `https://www.escook.cn/api/cart`
            // 发送异步请求
            const res = await axios({
    
     url: url });
            store.commit('setGoodsList', res.data.list) // 提交mutation修改state中的数据
        }
    },
    getters: {
    
    
        allCount(state) {
    
    
            return state.goodsList.reduce((sum, obj) => {
    
    
                if (obj.goods_state === true) {
    
     // 选中商品才累加数量
                    sum += obj.goods_count;
                }
                return sum;
            }, 0)
        },
        allPrice(state) {
    
    
            return state.goodsList.reduce((sum, obj) => {
    
    
                if (obj.goods_state) {
    
    
                    sum += obj.goods_count * obj.goods_price
                }
                return sum;
            }, 0)
        }
    }
}
export default cartModule

定义modules

语法:

modules: {
    
    
    模块名: 模块对象
}
  • 把2个模块对象, 引回到store里注册
import Vue from 'vue'
import Vuex from 'vuex'
import cartModule from './modules/cart'
import userModule from './modules/user'
Vue.use(Vuex)
const store = new Vuex.Store({
    
    
    modules: {
    
    
        user: userModule,
        cart: cartModule
    }
})
export default store

小结

  1. 为什么分模块?

    集中式管理项目过大, 变量过多, 会导致state臃肿, 难以维护

  2. 如何分模块?

    定义模块对象, state变成函数返回对象形式, 每个模块都有state/mutations/actions/getters/modules

  3. 根store如何注册?

    modules里 { 模块名: 模块对象 }

分模块-命名空间

目标

  • 防止多个模块之间, mutations/actions/getters的名字冲突

开启命名空间

在模块对象内设置namespaced: true

const moduleShopCar = {
    
    
    namespaced: true,
    state () {
    
    },
    mutations: {
    
    },
    actions: {
    
    },
    getters: {
    
    },
    modules: {
    
    }
}

state使用方式修改

  • 直接使用无变化: this.$store.state.模块名.变量名

  • 辅助函数需要遵守格式

    ...mapState("模块名", ['state变量名'])
    

mutations使用方式修改

  • 方式1: 组件内 - 直接使用

    • 原语法:

      this.$store.commit("mutations里的函数名", 具体值)
      
    • 开命名空间后语法:

      this.$store.commit("模块名/mutations里的函数名", 具体值)
      
  • 方式2: 组件内 - 映射使用

    • 原语法:

      ...mapMutations(['mutations里方法名'])
      
    • 开命名空间后语法:

      ...mapMutations("模块名", ['mutations里方法名'])
      

actions使用方式修改

  • 方式1: 组件内 - 直接使用

    • 原语法:

      this.$store.dispatch("actions里的函数名", 具体值)
      
    • 开命名空间后语法:

      this.$store.dispatch("模块名/actions里的函数名", 具体值)
      
  • 方式2: 组件内 - 映射使用

    • 原语法:

      ...mapActions(['actions里方法名'])
      
    • 开命名空间后语法:

      ...mapActions("模块名", ['actions里方法名'])
      

getters使用方式修改

  • 方式1: 组件内 - 直接使用

    • 原语法:

      this.$store.getters.计算属性名
      
    • 开命名空间后语法:

      this.$store.getters['模块名/计算属性名']
      
  • 方式2: 组件内 - 映射使用

    • 原语法:

      ...mapGetters(['getters里计算属性名'])
      
    • 开命名空间后语法:

      ...mapGetters("模块名", ['getters里计算属性名'])
      

小结

  1. state和mutations, 在根store和开启命名空间里的区别?

在这里插入图片描述

  1. 整个vuex的体系是?

    在这里插入图片描述

扩展: 使用Devtools调试vuex数据

优秀的调试工具可以使我们写程序事半功倍,最后我们再学习一下如果使用dev-tools来调试vuex中的数据,这也是数据可预测特性里不可缺少的一环

目标

  • 掌握dev-tools调试vuex
  • 理解什么是数据状态是可追踪的

如何进行调试

注意只有vue+vuex的项目才可以用

在这里插入图片描述

调试信息说明

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_48585264/article/details/119521624