12-Vue技术栈之Vuex的使用

news/2024/11/28 22:43:21/

目录

  • 1、理解 vue
    • 1.1 vuex 是什么
    • 1.2 什么时候使用 Vue
    • 1.3 图解两种方式实现数据共享
  • 2、搭建vuex环境
    • 2.1 下载vuex
    • 2.2 配置文件
  • 3、基本使用
    • 3.1 求和案例纯vue写法
    • 3.2 求和案例vuex写法
  • 4、getters的使用
  • 5、四个map方法的使用
    • 5.1 求和案例
  • 6、 模块化+命名空间
    • 6.1求和案例改造

1、理解 vue

1.1 vuex 是什么

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

1.2 什么时候使用 Vue

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

简单来说就是多个组件需要共享数据时

1.3 图解两种方式实现数据共享

  • 使用全局事件总线实现多组件共享数据较为麻烦。

请添加图片描述

  • 使用Vuex实现多组件共享数据简便。

请添加图片描述

2、搭建vuex环境

2.1 下载vuex

npm i vuex

在这里插入图片描述

注:
如果执行npm i vuex 默认安装的是vuex4版本,而vuex4版本只能在vue3里面使用,所以如果你用的是vue2需要指定安装vuex3的版本。

vue2里正确安装:

npm i vuex@3

2.2 配置文件

  1. 创建文件:src/store/index.js
//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex插件
Vue.use(Vuex)//准备actions对象——响应组件中用户的动作
const actions = {}
//准备mutations对象——修改state中的数据
const mutations = {}
//准备state对象——保存具体的数据
const state = {}//创建并暴露store
export default new Vuex.Store({actions,mutations,state
})
  1. main.js中创建vm时传入store配置项
......
//引入Vue
import Vue from 'vue'
//引入App
import App from './App.vue'
//引入store
import store from './store/index.js'
......//创建vm
new Vue({el:'#app',render: h => h(App),store
})

3、基本使用

  • vuex工作原理图:

在这里插入图片描述

  1. 初始化数据、配置actions、配置mutations,操作文件store.js
//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//引用Vuex
Vue.use(Vuex)const actions = {//响应组件中加的动作jia(context,value){// console.log('actions中的jia被调用了',miniStore,value)context.commit('JIA',value)},
}const mutations = {//执行加JIA(state,value){// console.log('mutations中的JIA被调用了',state,value)state.sum += value}
}//初始化数据
const state = {sum:0
}//创建并暴露store
export default new Vuex.Store({actions,mutations,state,
})
  1. 组件中读取vuex中的数据:$store.state.sum

  2. 组件中修改vuex中的数据:$store.dispatch('action中的方法名',数据)$store.commit('mutations中的方法名',数据)

    备注:若没有网络请求或其他业务逻辑,组件中也可以越过actions,即不写dispatch,直接编写commit

3.1 求和案例纯vue写法

  • 我们来用纯Vue写一个案例,在用vuex重新书写一遍,形成一个对比。

实现效果:

请添加图片描述
结构目录:
在这里插入图片描述

代码示例:
main 文件

import Vue from 'vue'
import App from './App.vue'
Vue.config.productionTip = falsenew Vue({render: h => h(App),
}).$mount('#app')

App组件

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

Count组件

<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></select><button @click="add">+</button><button @click="subtract">-</button><button @click="addOdd">当前和为奇数再加</button><button @click="addWait">等一等再加</button></div>
</template><script>
export default {name: "Count",data() {return {n: 1, //选择数sum: 0, //求和数};},methods: {add() {this.sum += this.n;},subtract() {this.sum -= this.n;},addOdd() {if (this.sum % 2) {this.sum += this.n;}},addWait() {setTimeout(() => {this.sum += this.n;}, 500);},},
};
</script><style>
h1 {color: pink;
}
select {width: 50px;background: pink;
}
button {margin: 5px;padding: 5px 10px;background: pink;border: none;cursor: pointer;
}
</style>

3.2 求和案例vuex写法

实现效果:

请添加图片描述
结构目录:

在这里插入图片描述
main文件

import Vue from 'vue'
import App from './App.vue'
//引入store里面的index.js文件,vue里默认引入的就是index文件
import store from './store'
Vue.config.productionTip = false
const vm = new Vue({render: h => h(App),store //安装store
}).$mount('#app')
console.log(vm);

App组件

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

Count组件

<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="add">+</button><button @click="subtract">-</button><button @click="addOdd">当前和为奇数再加</button><button @click="addWait">等一等再加</button></div>
</template><script>
export default {name: "Count",data() {return {n: 1, //选择数};},methods: {add() {this.$store.commit("ADD", this.n);},subtract() {this.$store.commit("SUBTRACT", this.n);},addOdd() {this.$store.dispatch("addOdd", this.n);},addWait() {this.$store.dispatch("addWait", this.n);},},
};
</script><style>
h1 {color: pink;
}
select {width: 50px;background: pink;
}
button {margin: 5px;padding: 5px 10px;background: pink;border: none;cursor: pointer;
}
</style>

store里面的index.js 文件

//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex插件
Vue.use(Vuex)//准备actions对象——响应组件中用户的动作
const actions = {addOdd(context, value) {if (context.state.sum % 2) {context.commit('ADD', value)};},addWait(context, value) {setTimeout(() => {context.commit('ADD', value)}, 500);}
}
//准备mutations对象——修改state中的数据
const mutations = {ADD(context, value) {context.sum += value},SUBTRACT(context, value) {context.sum -= value},}
//准备state对象——保存具体的数据
const state = {sum: 0, //求和数
}//创建并暴露store
export default new Vuex.Store({actions,mutations,state
})

4、getters的使用

  1. 概念:当state中的数据需要经过加工后再使用时,可以使用getters加工。

  2. store.js中追加getters配置

    ......const getters = {bigSum(state){return state.sum * 10//靠返回值}
    }//创建并暴露store
    export default new Vuex.Store({......getters
    })
    
  3. 组件中读取数据:$store.getters.bigSum

5、四个map方法的使用

  1. mapState方法:用于帮助我们映射state中的数据为计算属性
computed: {//借助mapState生成计算属性:sum、school、subject(对象写法)...mapState({sum:'sum',school:'school',subject:'subject'}),//借助mapState生成计算属性:sum、school、subject(数组写法)...mapState(['sum','school','subject']),
},
  1. mapGetters方法:用于帮助我们映射getters中的数据为计算属性
computed: {//借助mapGetters生成计算属性:bigSum(对象写法)...mapGetters({bigSum:'bigSum'}),//借助mapGetters生成计算属性:bigSum(数组写法)...mapGetters(['bigSum'])
},
  1. mapActions方法:用于帮助我们生成与actions对话的方法,即:包含$store.dispatch(xxx)的函数
methods:{//靠mapActions生成:incrementOdd、incrementWait(对象形式)...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})//靠mapActions生成:incrementOdd、incrementWait(数组形式)...mapActions(['jiaOdd','jiaWait'])
}
  1. mapMutations方法:用于帮助我们生成与mutations对话的方法,即:包含$store.commit(xxx)的函数
methods:{//靠mapActions生成:increment、decrement(对象形式)...mapMutations({increment:'JIA',decrement:'JIAN'}),//靠mapMutations生成:JIA、JIAN(对象形式)...mapMutations(['JIA','JIAN']),
}

备注:在四个方法使用时,若需要传递参数需要:在模板中绑定事件时传递好参数,否则参数是事件对象。

5.1 求和案例

  • 我们将上面的求和案例利用这些方法进行改进一下:
  • 其他文件照常,只改变store里面的index.jsCount组件
    index.js文件
//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex插件
Vue.use(Vuex)//准备actions对象——响应组件中用户的动作
const actions = {/* add(context, value) {context.commit('ADD', value)},subtract(context, value) {context.commit('SUBTRACT', value)}, */addOdd(context, value) {if (context.state.sum % 2) {context.commit('ADD', value)};},addWait(context, value) {setTimeout(() => {context.commit('ADD', value)}, 500);}
}
//准备mutations对象——修改state中的数据
const mutations = {ADD(context, value) {context.sum += value},SUBTRACT(context, value) {context.sum -= value},}
//准备state对象——保存具体的数据
const state = {sum: 0, //求和数school:'哔站',subject:'前端'
}
// 对state对象里的数据进行加工
const getters = {bigSum(state) {return state.sum * 10}
}//创建并暴露store
export default new Vuex.Store({actions,mutations,state,getters
})

Count组件

<template><div><h1>当前求和为:{{ sum }}</h1><h3>放大十倍后的和为:{{ bigSum }}</h3><h3>我在{{ school }}学{{ subject }}</h3><select v-model.number="n"><option value="1">1</option><option value="2">2</option><option value="3">3</option></select><button @click="add(n)">+</button><button @click="subtract(n)">-</button><button @click="addOdd(n)">当前和为奇数再加</button><button @click="addWait(n)">等一等再加</button></div>
</template><script>
// 导入4个map方法
import { mapState, mapGetters, mapMutations, mapActions } from "vuex";
export default {name: "Count",data() {return {n: 1, //选择数};},computed: {//靠程序员自己亲自去写计算属性/* sum(){return this.$store.state.sum},school(){return this.$store.state.school},subject(){return this.$store.state.subject}, *///借助mapState生成计算属性,从state中读取数据。(对象写法)// ...mapState({ sum: "sum", school: "school", subject: "subject" }),//借助mapState生成计算属性,从state中读取数据。(数组写法)// 数组写法必须数据名和计算方法名一致...mapState(["sum", "school", "subject"]),// ...............................//靠程序员自己亲自去写计算属性/* bigSum(){return this.$store.getters.bigSum}, *///借助mapGetters生成计算属性,从getters中读取数据。(对象写法)// ...mapGetters({bigSum:'bigSum'})//借助mapGetters生成计算属性,从getters中读取数据。(数组写法)...mapGetters(["bigSum"]),},methods: {//程序员亲自写方法/*   add() {this.$store.commit("ADD", this.n);},subtract() {this.$store.commit("SUBTRACT", this.n);},addOdd() {this.$store.dispatch("addOdd", this.n);},addWait() {this.$store.dispatch("addWait", this.n);}, *///借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法)...mapMutations({ add: "ADD", subtract: "SUBTRACT" }), //必须在插值语法中传入实参,不然默认是event事件对象//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(数组写法)数组写法必须数据名和方法名一致,这里改了,上面调用的时候也要改。// ...mapMutations({ADD:'ADD',SUBTRACT:'SUBTRACT'})//借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(对象写法)// ...mapActions({addOdd:'addOdd',addWait:'addWait'})//借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(数组写法)...mapActions(["addOdd", "addWait"]),},
};
</script><style>
h1,
h3 {color: pink;
}
select {width: 50px;background: pink;
}
button {margin: 5px;padding: 5px 10px;background: pink;border: none;cursor: pointer;
}
</style>

6、 模块化+命名空间

  • 目的:让代码更好维护,让多种数据分类更加明确。

  • 修改store.js

    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}
    })
    
  • 开启命名空间后,组件中读取state数据:

    //方式一:自己直接读取
    this.$store.state.personAbout.list
    //方式二:借助mapState读取:
    ...mapState('countAbout',['sum','school','subject']),
    
  • 开启命名空间后,组件中读取getters数据:

    //方式一:自己直接读取
    this.$store.getters['personAbout/firstPersonName']
    //方式二:借助mapGetters读取:
    ...mapGetters('countAbout',['bigSum'])
    
  • 开启命名空间后,组件中调用dispatch

    //方式一:自己直接dispatch
    this.$store.dispatch('personAbout/addPersonWang',person)
    //方式二:借助mapActions:
    ...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
    
  • 开启命名空间后,组件中调用commit

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

6.1求和案例改造

  • 实现效果:

请添加图片描述

  • 结构目录:

在这里插入图片描述
main文件

import Vue from 'vue'
import App from './App.vue'
import store from './store'Vue.config.productionTip = falsenew Vue({render: h => h(App),store
}).$mount('#app')

App组件

<template><div><Count /><Person /></div>
</template><script>
import Count from "./components/Count";
import Person from "./components/Person";
export default {name: "App",components: { Count, Person },
};
</script>
<style>
</style>

Count组件使用4种map方法

<template><div><h1>当前求和为:{{ sum }}</h1><h3>放大十倍后的和为:{{ bigSum }}</h3><h3>我在{{ school }}学{{ subject }}</h3><h3 style="color: red">Person组件的总人数是:{{ persons.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="add(n)">+</button><button @click="subtract(n)">-</button><button @click="addOdd(n)">当前和为奇数再加</button><button @click="addWait(n)">等一等再加</button></div>
</template><script>
// 导入4个map方法
import { mapState, mapGetters, mapMutations, mapActions } from "vuex";
export default {name: "Count",data() {return {n: 1,};},computed: {//借助mapState生成计算属性,从state中读取数据。(数组写法)...mapState('CountOptions',["sum", "school", "subject"]),...mapState('PersonOptions',["persons"]),//借助mapGetters生成计算属性,从getters中读取数据。(数组写法)...mapGetters('CountOptions',["bigSum"]),},methods: {//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法)...mapMutations('CountOptions',{ add: "ADD", subtract: "SUBTRACT" }),//借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(对象写法)...mapActions('CountOptions',["addOdd", "addWait"]),},
};
</script><style>
h1,
h3,
li {color: pink;
}
select {width: 50px;background: pink;
}
button {margin: 5px;padding: 5px 10px;background: pink;border: none;cursor: pointer;
}
</style>

Person组件不使用map方法,和上面的写法进行一个对比

<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="addZhang">添加一个姓张的人</button><button @click="addPersonServer">随机添加一条语录</button><ul><li v-for="p in persons" :key="p.id">{{ p.name }}</li></ul></div>
</template><script>
import { nanoid } from "nanoid";
export default {name: "Person",data() {return {name: "",};},computed: {persons() {//   return this.$store.state.personsreturn this.$store.state.PersonOptions.persons;},sum() {return this.$store.state.CountOptions.sum;},firstPersonName() {return this.$store.getters["PersonOptions/firstPersonName"];},},methods: {add() {const personObj = { id: nanoid(), name: this.name };this.$store.commit("PersonOptions/PersonAdd", personObj);},addZhang() {const personObj = { id: nanoid(), name: this.name };this.$store.dispatch("PersonOptions/addZhang", personObj);},addPersonServer() {this.$store.dispatch('PersonOptions/addPersonServer')}},mounted() {console.log(this.$store);},
};
</script><style>
input::placeholder {color: pink;
}
input {border: 1px solid pink;padding: 5px;
}
</style>

store/index.js文件

//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
import axios from 'axios'
import { nanoid } from 'nanoid'
//应用Vuex插件
Vue.use(Vuex)const CountOptions = {namespaced: true,actions: {addOdd(context, value) {if (context.state.sum % 2) {context.commit('ADD', value)};},addWait(context, value) {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}}
}
const PersonOptions = {namespaced: true,actions: {addZhang(context, value) {if (value.name.indexOf('张') === 0) {context.commit('PersonAdd', value)} else { alert('请输入姓张的人') }},addPersonServer(context) {axios({methods: 'get',url: 'https://api.uixsj.cn/hitokoto/get?type=social'}).then(response => {context.commit('PersonAdd', { id: nanoid(), name: response.data })}, error => { alert(error.message)})}},mutations: {PersonAdd(state, value) {state.persons.unshift(value)}},state: {persons: [{ id: "001", name: '张三' }]},getters: {firstPersonName(state) {return state.persons[0].name}}
}//创建并暴露store
export default new Vuex.Store({modules: {CountOptions,PersonOptions}
})

store/index.js文件进行拆分

  • 目录结构
    在这里插入图片描述
    index.js
//引入Vue核心库
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'import CountOptions from './CountOptions'
import PersonOptions from './PersonOptions'
//应用Vuex插件
Vue.use(Vuex)//创建并暴露store
export default new Vuex.Store({modules: {CountOptions,PersonOptions}
})

CountOptions.js

export default {namespaced: true,actions: {addOdd(context, value) {if (context.state.sum % 2) {context.commit('ADD', value)};},addWait(context, value) {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}}
}

PersonOptions.js

import axios from 'axios'
import { nanoid } from 'nanoid'
export default {namespaced: true,actions: {addZhang(context, value) {if (value.name.indexOf('张') === 0) {context.commit('PersonAdd', value)} else { alert('请输入姓张的人') }},addPersonServer(context) {axios({methods: 'get',url: 'https://api.uixsj.cn/hitokoto/get?type=social'}).then(response => {context.commit('PersonAdd', { id: nanoid(), name: response.data })}, error => {alert(error.message)})}},mutations: {PersonAdd(state, value) {state.persons.unshift(value)}},state: {persons: [{ id: "001", name: '张三' }]},getters: {firstPersonName(state) {return state.persons[0].name}}
}

http://www.ppmy.cn/news/62807.html

相关文章

LeetCode:21. 合并两个有序链表

21. 合并两个有序链表 1&#xff09;题目2&#xff09;思路3&#xff09;代码4&#xff09;结果 1&#xff09;题目 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1&#xff1a; 输入&#xff1a;l1 [1,2,4], l2…

Spring Boot 3.x 系列【31】集成邮件发送

有道无术,术尚可求,有术无道,止于术。 本系列Spring Boot版本3.0.5 源码地址:https://gitee.com/pearl-organization/study-spring-boot3 文章目录 1. 前言2. 协议2.1 SMTP2.2 POP32.3 IMAP3. 邮件服务器4. 案例演示4.1 SMTP 服务器4.2 集成4.3 测试1. 前言 电子邮件是一…

Vue3学习笔记(尚硅谷)

文章目录 一、创建vue3工程1-1、使用vite创建vue3项目1-1、安装开发者工具 二、常用Composition API2-1、setup2-2、ref函数2-3、reactive函数2-4、Vue3的响应式原理2-4-1.Vue2的响应式原理2-4-3.Vue3的响应式原理 2-5、reactive对比ref2-6、setup的两个注意点2-7、计算属性与监…

asp.net+sqlserver漫画绘本借阅管理系统

摘 要1 第1章 系统概述5 1.1 研究背景5 1.2 研究的意义5 1.3 主要研究内容5 第2章 系统开发环境7 2.1 ASP.NET概述7 2.2 动态网站技术介绍8 2.3 数据库技术8 第3章 需求分析9 3.1 需求分析9 3.1.1 功能需求9 3.2 可行性分析9 3.2.1 可行性分析9 3.2.2 技术可行性9 3.2.3 运行可…

2023全栈开发人员职业路线图

0. 全栈开发人员职业路线图 全栈开发人员是IT行业中薪资最高的职业之一。 如果您想成为一名全栈开发人员&#xff0c;以下是2023年全栈开发人员路线图上的十一个步骤&#xff1a; 掌握敏捷开发和Scrum学习浏览器技术&#xff0c;如HTML和CSS熟练掌握JavaScript或TypeScript了…

Linux权限划分的原则

考察的不仅是一个具体的指令&#xff0c;还考察对技术层面的认知。 如果对 Linux 权限有较深的认知和理解&#xff0c;那么完全可以通过查资料去完成具体指令的执行。更重要的是&#xff0c;认知清晰的程序员可以把 Linux 权限管理的知识迁移到其他的系统设计中。 权限抽象 一…

真实业务场景使用-模板模式+策略模式组合

模板和策略设计模式一般是使用最频繁的设计模式&#xff0c;模板的场景主要是处理一系列相同的流程&#xff0c;将这些流程放到模板里&#xff0c;每个流程里的处理可能有一些不一样的地方&#xff0c;则可以抽象出一个方法&#xff0c;由每一个有实际意义的子类实现。 策略模…

C# 判断文件/目录是否存在

C# 判断文件是否存在&#xff0c;不存在则创建 C#判断指定目录是否存在&#xff0c;不存在就创建 spath&#xff1a;文件夹路径名 using System.IO; if (Directory.Exists(spath)) { } else { DirectoryInfo directoryInfo new DirectoryInfo(spath); directoryInfo.…