Vuex学习与应用

本文介绍了Vuex在Vue框架中的应用,重点讲解了如何通过v-bind、v-on和EventBus在组件间共享数据,以及Vuex的安装、基本使用、核心概念(state、mutations、actions和getters)及其在TODOS案例中的实际操作。
摘要由CSDN通过智能技术生成

笔记总结来源:视频学习

组件之间共享数据的方式

  • 父向子传值:v-bind 属性绑定。
  • 子向父传值:v-on 事件绑定。
  • 兄弟组件之间共享数据:EventBus ($on 接收数据的那个组件 ; $emit 发送数据的那个组件)。

上面这三种方式只适用于小范围的。而Vuex适合大范围数据频繁共享。

一、Vuex概述

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

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

  • 能够在Vuex中集中管理共享的数据,易于开发和后期维护;
  • 能够高效地实现组件之间的数据共享,提高开发效率;
  • 存储在Vuex中的数据都是响应式的,能够实时保持数据与页面的同步。

二、Vuex的基本使用

2.1 安装依赖包

npm i vuex -S

2.2 在store文件夹中index.js导入Vuex包,

import Vue from 'vue'

import Vuex from 'vuex'

Vue.use(Vuex)

//创建store对象

export default new Vuex.Store({

  state: {

  },

  getters: {

  },

  mutations: {

  },

  actions: {

  },

  modules: {

  }

})

2.3 在main.js将store对象挂载到vue实例中

import Vue from 'vue'

import App from './App.vue'

import router from './router'

import store from './store'

Vue.config.productionTip = false

new Vue({

  router,

  store,

  render: h => h(App)

}).$mount('#app')

三、Vuex的核心概念

3.1 State

        定义:State提供唯一的公共数据源,所有共享的数据都要统一放到Store的State中进行存储。eg: 定义一个count值。

export default new Vuex.Store({

  state: {

    count:0,

  }

})

        3.1.1 组件访问State中数据的第一种方式

 在 template 中:{{ $store.state.count }}

 在 js 中:  this.$store.state.count

        3.1.2 组件访问State中数据的第二种方式

<template>

  <div>

    <!-- 3、通过插值表达式显示计算属性的值 -->

    <h3>当前最新的count值为:{{ count}}</h3>

    <button>-1</button>

  </div>

</template>

<script>

import { mapState } from 'vuex'  // 1、从vuex中按需导入 mapState 函数

export default {

    data(){return {}},

    computed:{

        ...mapState( ['count'] )    // 2、通过展开运算符,mapState函数将当前组件需要的全局数据映射为当前组件的computed计算属性

    }

}

</script>

3.2 Mutation

        定义: 用于变更Store中的数据。

  • 直接在组件中操作Store中的数据会不规范,代码量大的时候很难维护,找不到是哪个组件修改了Store的值,因此,建议只通过mutation 变更Store数据。
  • 只有mutations中定义的函数,才有权利修改 state 中的数据
  • 通过这种方式虽然操作起来稍微繁琐一些,但是可以集中监控所有数据的变化。
  • 不能在mutations的函数中执行异步代码,mutations中的函数处理的是同步任务

3.2.1 变更state对象第一种方式——commit()

 组件中this.$store.commit();commit的作用 ,就是调用 某个mutation 函数

当组件触发mutations时传递参数

 3.2.2 变更state对象第二种方式——mapMutations()

3.3 Action

Action用于处理异步任务

  • 如果通过异步操作变更数据,必须通过Action,而不能使用Mutation。
  • 在Action中不能直接更改state中的数据,要通过触发Mutations的方式间接变更数据,。

 3.3.1 触发Action第一种方式——dispatch()

  state: {

    count:0,

  },

  mutations: {

    add(state){  

      // 变更状态

      state.count += 3

    },

  },

  actions: {

    addAsync(context){

      setTimeout(() => {

        // commit()里的方法只能是mutations中的函数

        context.commit('add')

      },3000)

    }

  },

<template>

  <div>

    <h3>当前最新的count值为:{{$store.state.count}}</h3>

    <button @click="handler2">延迟3后执行+1</button>

  </div>

</template>

<script>

export default {

    data(){

        return {

        }

    },

    methods:{

        handler2(){

            // 触发Action,不携带参数时

            this.$store.dispatch('addAsync')

        }

    }

}

</script>

 3.3.2 触发Action第二种方式——mapActions()

3.4 Getter

定义:Getter用于对Store中的数据进行加工处理形成新的数据,不会修改Store中的原数据,类似起到了包装作用。

  • Getter可以对Store中已有的数据加工处理之后形成新的数据,类似Vue的计算属性。
  • Store中数据发生变化,Getter的数据也会跟着变化。

export default new Vuex.Store({

  state: {

    count:0,

  },

  getters: {

    showNum:state => {

      return '当前最新的数量是【'+state.count+'】'

    }

  }

})

3.4.1 使用getters的第一种方式

 在 template 中:{{ $store.getters.showNum}}

 在 js 中:  this.$store.getters.showNum

3.4.1 使用getters的第二种方式

<template>

  <div>

    <!-- 3、通过插值表达式显示计算属性的值 -->

    <h3>getters修饰过的:{{ showNum}}</h3>

  </div>

</template>

<script>

import { mapGetters } from 'vuex' 

export default {

    computed:{

        ...mapGetters ( ['showNum'] )   

    }

}

</script>

os:以上举例完整代码如下:

1、store文件夹里的index.js

export default new Vuex.Store({
  state: {
    count:0,
  },
  mutations: {
    add(state){  
      // 变更状态
      state.count ++
    },
    addN(state,step){  
      // 变更状态
      state.count += step
    },
    subFun(state){  
      // 变更状态
      state.count --
    },
    subNFun(state,step){  
      // 变更状态
      state.count -= step
    }
  },
  actions: {
    addAsync(context){
      setTimeout(() => {
        // commit()里的方法只能是mutations中的函数
        context.commit('add')
      },1000)
    },
    addNAsync(context,step){
      setTimeout(() => {
        // commit()里的方法只能是mutations中的函数
        context.commit('addN',step)
      },3000)
    },
    subAsync(context){
      setTimeout(() => {
        // commit()里的方法只能是mutations中的函数
        context.commit('subFun')
      },1000)
    },
    subNAsync(context,step){
      setTimeout(() => {
        // commit()里的方法只能是mutations中的函数
        context.commit('subNFun',step)
      },3000)
    }
  },
  getters: {
    showNum:state => {
      return '当前最新的数量是【'+state.count+'】'
    }
  },
  modules: {
  }
})

2、执行加法组件的代码如下:

<template>
  <div>
    <h3>当前最新的count值为:{{$store.state.count}}</h3>
    <h3>getters修饰过的:{{$store.getters.showNum}}</h3>
    <button @click="handler1">+1</button>
    <button @click="handler2">延迟1后执行+1</button>
    <button @click="handler3">延迟3后执行+3</button>
  </div>
</template>

<script>
export default {
    data(){
        return {

        }
    },
    methods:{
        handler1(){
            // 触发mutations
            this.$store.commit('add')
        },
        handler2(){
            // 触发Action
            this.$store.dispatch('addAsync')
        },
        handler3(){
            // 触发Action
            this.$store.dispatch('addNAsync',3)
        }
    }
}
</script>

<style>

</style>

3、执行减法的代码如下:

<template>
  <div>
    <!-- 3、通过插值表达式显示计算属性的值 -->
    <h3>当前最新的count值为:{{count}}</h3>
    <h3>getters修饰过的:{{showNum}}</h3>
    <button @click="handler2">-1</button>
    <button @click="handler3">-N</button>
    <button @click="handler4">异步-1</button>
    <button @click="handler5">异步-N</button>
  </div>
</template>

<script>

import {mapGetters,mapActions,mapMutations,mapState} from 'vuex'  // 1、从vuex中按需导入 mapState 函数

export default {
    data(){
        return {
        }
    },
    computed:{
        ...mapGetters(['showNum']),
        ...mapState(['count'])  // 2、通过展开运算符,mapState函数将当前组件需要的全局数据映射为当前组件的computed计算属性
    },
    methods:{
        ...mapMutations(['subFun','subNFun']),  //触发mutations的第二种方式
        ...mapActions(['subAsync','subNAsync']),  //触发Actions的第二种方式
        handler2(){
            this.subFun()
        },
        handler3(){
            this.subNFun(3)
        },
        handler4(){
            this.subAsync()
        },
        handler5(){
            this.subNAsync(3)
        }
    }
}
</script>

四、基于Vuex的案例——TODOS

涉及到的技术栈:vue2  、Ant Design vue 、axios 、vuex

4.1 安装依赖

        npm i axios -S

        npm i ant-design-vue@1.7.8 -S

注意:

如果目前使用 npm i --save ant-design-vue 命令安装的 Ant Design vue 版本为最新版本,适用于 Vue3.x 项目。如果我们 要使用 Vue2.x 来开发项目时,就必须安装指定 Ant Design vue 的版本号, 否则会报错 。如果您是 Vue2.x 环境下,必须使用 npm i ant-design-vue@1.7.8。

4.2 配置安装好的依赖

在main.js中配置

// 1、导入 ant-design-vue 组件库

import Antd from 'ant-design-vue';

// 2、导入组件库的样式表

import 'ant-design-vue/dist/antd.css';

Vue.config.productionTip = false

// 安装组件库

Vue.use(Antd);

 4.3 最基本的UI结构

<template>
  <div class="box">
    <div>
      <a-input placeholder="请输入任务" class="my_ipt" />    
      
      <a-list bordered :dataSource="list" class="dt_list">
        <a-list-item slot="renderItem" slot-scope="item">
          <!-- 复选框 -->
          <a-checkbox>{{ item.info }}</a-checkbox>
          <!-- 删除链接 -->
          <a slot="actions">删除</a>
        </a-list-item>

        <!-- footer区域 -->
        <div class="footer" slot="footer">
          <span>0条剩余</span>
          <a-button-group>
            <a-button type="primary">全部</a-button>
            <a-button>未完成</a-button>
            <a-button>已完成</a-button>
          </a-button-group>
          <a>清除已完成</a>
        </div>
      </a-list>
    </div>
    <a-button type="primary">添加事项</a-button>

  </div>
</template>
<script>
export default {
  name: "HomeView",
  data() {
    return {
      list: [
        {
          id: 0,
          info: "Racing car sprays burning fuel into crowd.",
          done: false,
        },
        {
          id: 1,
          info: " Japanese princess to wed commoner.",
          done: false,
        },
        {
          id: 2,
          info: "Australian walks 100km after outback crash.",
          done: false,
        },
        {
          id: 3,
          info: "Man charged over missing wedding girl.",
          done: false,
        },
        {
          id: 4,
          info: "Los Angeles battles huge wildfires.",
          done: false,
        },
      ],
    };
  },
};
</script>
<style scoped>

.box {
    display: flex;
    justify-content: center;
    margin-top: 50px;
}
.my_ipt {
  width: 500px;
  margin-right: 10px;
}
.dt_list {
  width: 500px;
  margin-top: 10px;
}
.footer {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
</style>

4.4 功能实现

<template>
  <div class="box">
    <div>
      <a-input
        :value="inputValue"
        @change="handleInputChange"
        placeholder="请输入任务"
        class="my_ipt"
      />

      <a-list bordered :dataSource="infolist" class="dt_list">
        <a-list-item slot="renderItem" slot-scope="item">
          <!-- 复选框 -->
          <a-checkbox :checked="item.done" @change="cbOperateFun($event,item.id)">{{
            item.info
          }}</a-checkbox>
          <!-- 删除链接 -->
          <a slot="actions" @click="removeItemById(item.id)">删除</a>
        </a-list-item>

        <!-- footer区域 -->
        <div class="footer" slot="footer">
          <span>{{unDOneLength}}条剩余</span>
          <a-button-group>
            <a-button :type="viewKey == 'all' ? 'primary':'default'" @click="changeList('all')">全部</a-button>
            <a-button :type="viewKey == 'undone' ? 'primary':'default'" @click="changeList('undone')">未完成</a-button>
            <a-button :type="viewKey == 'done' ? 'primary':'default'" @click="changeList('done')">已完成</a-button>
          </a-button-group>
          <a @click="cleanFun">清除已完成</a>
        </div>
      </a-list>
    </div>

    <a-button type="primary" @click="addItemToList">添加事项</a-button>
  </div>
</template>
<script>
import {mapState,mapGetters} from 'vuex'
export default {
  name: "HomeView",
  data() {
    return {
    };
  },
  computed:{
    ...mapState(['inputValue','viewKey']),
    ...mapGetters(['unDOneLength','infolist'])
  },
  created(){
    this.$store.dispatch("getList")
  },
  methods:{
    // 监听文本框内容变化
    handleInputChange(e){
      // console.log('ee',e.target.value);
      this.$store.commit('setInputValue',e.target.value)
    },
    // 向列表中新增item项
    addItemToList(){
      if(this.inputValue.trim().length <= 0){
        return this.$message.warning('文本框内容不能为空!')
      }
      this.$store.commit('addItem')
    },
    //向列表中删除item项
    removeItemById(id){
      this.$store.commit('delItem',id)
    },
    // 复选框操作
    cbOperateFun(e,id){
      // 通过e.target可以得到最新的选中状态
      // console.log('e',e.target,id);
      const param = {
        id:id,
        status:e.target.checked
      }
      this.$store.commit('changeStatus',param)
    },
    //清除已完成的任务
    cleanFun(){
      this.$store.commit('cleanDone')
    },
    // 修改页面上展示列表的数据
    changeList(key){
      this.$store.commit('changeViewKey',key)
    }

  }
};
</script>
<style scoped>
.box {
  display: flex;
  justify-content: center;
  margin-top: 50px;
}
.my_ipt {
  width: 500px;
  margin-right: 10px;
}
.dt_list {
  width: 500px;
  margin-top: 10px;
}
.footer {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
</style>
import Vue from 'vue'
import Vuex from 'vuex'
import axios from 'axios'


Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    list:[],  //所有的任务列表
    inputValue:'',
    nextId:5 , //下一个ID
    viewKey:'all'
  },
  mutations: {
    initList(state,list_){
      state.list = list_
    },
    // 为store中的inputValue赋值
    setInputValue(state,val){
      state.inputValue = val
    },
    // 添加列表项
    addItem(state){
      const obj = {
        id:state.nextId,
        info:state.inputValue.trim(),
        done:false
      }
      state.list.unshift(obj);
      state.nextId++;
      state.inputValue = ''
    },
    // 删除列表项
    delItem(state,item_id){
      let index = state.list.findIndex(item => item.id == item_id)
      // 根据索引,删除对应的元素
      if(index !== -1){
        state.list.splice(index,1);
      }
      state.inputValue = ''
    },
    // 修改列表项的选中状态
    changeStatus(state,param){
      const index = state.list.findIndex(item => item.id == param.id)
      if(index !== -1){
        state.list[index].done = param.status
      }
    },
    // 清除已完成的任务列表
    cleanDone(state){
      state.list = state.list.filter(item => item.done === false)
    },
    // 修改视图的关键字
    changeViewKey(state,key){
      state.viewKey = key
    }
  
  },
  actions: {
    getList(context){
      axios.get('/list.json').then(({data}) => {
        console.log(data);
        context.commit('initList',data)
      })
    },
  },
  getters: {
    // 统计未完成的任务条数
    unDOneLength:state => {
      return state.list.filter(item => item.done === false).length
    },
    // 
    infolist:state => {
      if(state.viewKey == 'all'){
        return state.list
      }
      if(state.viewKey == 'undone'){
        return state.list.filter(item => !item.done)
      }
      if(state.viewKey == 'done'){
        return state.list.filter(item => item.done)
      }
      return state.list
    }
  }
})

  • 25
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

火乐暖阳85105

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值