FrontEnd笔记 -- Vuex

本文详细介绍了Vuex在Vue.js应用中的作用,包括集中式状态管理、组件间通信、求和案例的实现,以及Vuex的安装、基本配置、getters、map方法的使用、多组件共享数据、模块化和命名空间的应用。通过一个终极案例展示了Vuex如何在实际项目中管理状态,实现多组件间的高效协同。
摘要由CSDN通过智能技术生成

七、Vuex

  • 概念
  1. 专门在 Vue 中实现 集中式状态(数据)管理 的一个 Vue 插件;
  2. 对 vue 应用中多个组件的共享状态进行集中式的管理(读/写);
  3. 也是一种组件间通信的方式,且适用于任意组件间通信
  • 使用场景
  1. 多个组件依赖于同一状态
  2. 来自不同组件的行为需要变更同一状态
  • 多组件共享数据
  1. 全局事件总线实现

在这里插入图片描述

  1. vuex 实现

在这里插入图片描述

7.1 求和案例

在这里插入图片描述

7.1.1 纯 vue 版

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

<script>
	export default {
		name:'Count',
		data() {
			return {
				n: 1, // 当前用户选择的数字
				sum: 0 // 当前的和
			}
		},
		methods: {
			increment() {
				this.sum += this.n
			},
			decrement() {
				this.sum -= this.n
			},
			incrementOdd() {
				if(this.sum % 2)
					this.sum += this.n

			},
			incrementWait() {
				setTimeout(() => {
					this.sum += this.n
				}, 500);
			}
		}
	}
</script>

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

7.1.2 Vuex 工作原理

在这里插入图片描述

7.1.3 搭建 Vuex 环境

  1. 安装 vuex
PS D:\workspace\vscode\vue_test> npm i vuex

+ vuex@3.6.2
added 1 package from 1 contributor in 6.492s

PS D:\workspace\vscode\vue_test> 
  1. 创建文件:src/store/index.js
// 该文件用于创建 Vuex 中最为核心的 store

// 引入 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 配置项
import Vue from 'vue'
import App from './App.vue'

// 引入 store
import store from './store'

Vue.config.productionTip = false

new Vue ({
    el: '#app',
    render: h => h(App),
    store,
    beforeCreate() {
        Vue.prototype.$bus = this
    }
})

7.1.4 vuex 版

<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:'Count',
		data() {
			return {
				n: 1
			}
		},
		methods: {
			// 无业务逻辑可以直接调用 commit
			increment() {
				this.$store.commit('INCREMENT', this.n)
			},
			decrement() {
				this.$store.commit('DECREMENT', this.n)
			},
			
			incrementOdd() {
				this.$store.dispatch('incrementOdd', this.n)
			},
			incrementWait() {
				this.$store.dispatch('incrementWait', this.n)
			}
		}
	}
</script>

<style scoped>
	button {
		margin-left: 5px;
	}
</style>
// 该文件用于创建 Vuex 中最为核心的 store

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

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

// 准备 actions -- 用于响应组件中的动作
const actions = {
    // 无业务逻辑处理可以省略不写
    /*increment(context, value) {
        context.commit('INCREMENT', value)
    },
    decrement(context, value) {
        context.commit('DECREMENT', value)
    },*/
    incrementOdd(context, value) {
        if(context.state.sum % 2)
            context.commit('INCREMENT', value)
    },
    incrementWait(context, value) {
        setTimeout(() => {
            context.commit('INCREMENT', value)
        }, 500)
    }
}

// 准备 mutations -- 用于操作数据(state)
const mutations = {
    INCREMENT(state, value) {
        state.sum += value
    },
    DECREMENT(state, value) {
        state.sum -= value
    }
}

// 准备 state -- 用于存储数据
const state = {
    sum: 0
}

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

7.2 getters 配置项

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

  • store.js 中追加 getters 配置
......

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

//创建并暴露store
export default new Vuex.Store({
	......
	getters
})
  • 组件中读取数据:
$store.getters.bigSum

7.3 四个map方法的使用

// src/store/index.js

...
//准备state——用于存储数据
const state = {
	sum:0, //当前的和
	school:'尚硅谷',
	subject:'前端'
}
...
<template>
	<div>
		<h1>当前求和为:{{sum}}</h1>
		<h3>当前求和放大10倍为:{{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>
		<!-- mapActions与mapMutations使用时,若需要传递参数需要:在模板中绑定事件时传递好参数,否则参数是事件对象。 -->
		<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:'Count',
		data() {
			return {
				n:1, //用户选择的数字
			}
		},
		computed:{
			//借助mapState生成计算属性,从state中读取数据。(对象写法)
			// ...mapState({sum:'sum',school:'school',subject:'subject'}),

			//借助mapState生成计算属性,从state中读取数据。(数组写法)
			...mapState(['sum','school','subject']),

			/* ******************************************************************** */

			//借助mapGetters生成计算属性,从getters中读取数据。(对象写法)
			// ...mapGetters({bigSum:'bigSum'})
			
			//借助mapGetters生成计算属性,从getters中读取数据。(数组写法)
			...mapGetters(['bigSum'])
		},
		methods: {
			//程序员亲自写方法
			/* increment(){
				this.$store.commit('JIA',this.n)
			},
			decrement(){
				this.$store.commit('JIAN',this.n)
			}, */

			//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法)
			...mapMutations({increment:'INCREMENT',decrement:'DECREMENT'}),

			//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(数组写法)
			// ...mapMutations(['INCREMENT','DECREMENT']),

			/* ************************************************* */

			//程序员亲自写方法
			/* incrementOdd(){
				this.$store.dispatch('jiaOdd',this.n)
			},
			incrementWait(){
				this.$store.dispatch('jiaWait',this.n)
			}, */

			//借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(对象写法)
			...mapActions({incrementOdd:'incrementOdd',incrementWait:'incrementWait'})

			//借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(数组写法)
			// ...mapActions(['incrementOdd','incrementWait'])
		},
		mounted() {
			const x = mapState({he:'sum',xuexiao:'school',xueke:'subject'})
			console.log(x)
		},
	}
</script>

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

7.5 多组件共享数据

  • 新增 Person.vue
<template>
    <div>
        <h1>人员列表</h1>
        <input type="text" placeholder="请输入名字" v-model="name">
        <button @click="addPerson">添加</button>
        <ul>
            <li v-for="person in personList" :key="person.id">{{person.name}}</li>
        </ul>
    </div>
</template>

<script>
    import {mapState} from 'vuex'
    import {nanoid} from 'nanoid'

    export default {
        name: 'Person',
        computed: {
            ...mapState(['personList'])
        },
        data() {
            return {
                name: ''
            }
        },
        methods: {
            addPerson() {
                const personObj = {id: nanoid(), name: this.name}
                this.$store.commit('ADD_PERSON', personObj)
                this.name = ''
            }   
        }
    }
</script>
  • 修改 store.js
...

const mutations = {
    ...
    ADD_PERSON(state, value) {
        state.personList.unshift(value)
    }
}

const state = {
    ...
    personList: [
        {id: '001', name: '张三'}
    ]
}

...

在这里插入图片描述

  • 多组件共享数据

在这里插入图片描述

<template>
    <div>
        ...
        <h1 style="color: red">Count 组件当前求和为:{{sum}}</h1>
    </div>
</template>

<script>
    ...
    export default {
        name: 'Person',
        computed: {
            ...mapState(['personList', 'sum'])
        },
       ...
</script>
<template>
	<div>
		...
		<h1 style="color: red">Person 组件当前人数为:{{personList.length}}</h1>
	</div>
</template>

<script>
	...
	export default {
		name:'Count',
		...
		computed: {
			...mapState(['sum','school','subject', 'personList']),
			...mapGetters(['bigSum'])
		},
		...
	}
</script>
...

7.6 模块化和 namespace

  • 组件化 store.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

// 求和组件相关的配置
const countOptions = {
    namespaced: true,
    actions: {
        incrementOdd(context, value) {
            if(context.state.sum % 2)
                context.commit('INCREMENT', value)
        },
        incrementWait(context, value) {
            setTimeout(() => {
                context.commit('INCREMENT', value)
            }, 500)
        }
    },
    mutations: {
        INCREMENT(state, value) {
            state.sum += value
        },
        DECREMENT(state, value) {
            state.sum -= value
        }
    },
    state: {
        sum: 0,
        school: '尚硅谷',
        subject: '前端'
    },
    getters: {
        bigSum(state){
            return state.sum * 10
        }
    }
}

// 人员管理组件相关的配置
const personOptions = {
	// '命名空间' 必须开启后,组件中才能通过四个 map 快速的读取
    namespaced: true,
    actions: {},
    mutations: {
        ADD_PERSON(state, value) {
            state.personList.unshift(value)
        }
    },
    state: {
        personList: [
            {id: '001', name: '张三'}
        ]
    },
    getters: {}
}

export default new Vuex.Store({
    modules: {
        countOptions,
        personOptions
    }
})
  • 修改组件与 vuex 交互的引用方式
...
<script>
	...
	export default {
		name:'Count',
		...
		computed: {
			// 修改为从 vuex 模块化组件中读取数据
			// ...mapState(['sum','school','subject', 'personList']),
			...mapState('countOptions', ['sum', 'school', 'subject']),
			...mapState('personOptions', ['personList']),
			...mapGetters('countOptions', ['bigSum'])
		},
		methods: {
			// 修改为从 vuex 模块化组件中调用方法
			// ...mapMutations({increment:'INCREMENT',decrement:'DECREMENT'}),
			// ...mapActions({incrementOdd:'incrementOdd',incrementWait:'incrementWait'})
			...mapMutations('countOptions', {increment:'INCREMENT',decrement:'DECREMENT'}),
			...mapActions('countOptions', {incrementOdd:'incrementOdd',incrementWait:'incrementWait'})
		}
	}
</script>
...
...
<script>
    ...
    export default {
        name: 'Person',
        computed: {
            // 从 vuex 模块化中取数据
            // ...mapState(['personList', 'sum']),
            ...mapState('personOptions', ['personList']),
            ...mapState('countOptions', ['sum'])
        },
        ...
        methods: {
            addPerson() {
                const personObj = {id: nanoid(), name: this.name}
                // 从 vuex 模块化中调用方法
                // this.$store.commit('ADD_PERSON', personObj)
                this.$store.commit('personOptions/ADD_PERSON', personObj)
                this.name = ''
            }
        }
    }
</script>
  • 总结
  1. 开启命名空间后,组件中读取state数据:
//方式一:自己直接读取
this.$store.state.personAbout.list
//方式二:借助mapState读取:
...mapState('countAbout',['sum','school','subject']),
  1. 开启命名空间后,组件中读取getters数据:
//方式一:自己直接读取
this.$store.getters['personAbout/firstPersonName']
//方式二:借助mapGetters读取:
...mapGetters('countAbout',['bigSum'])
  1. 开启命名空间后,组件中调用dispatch
//方式一:自己直接dispatch
this.$store.dispatch('personAbout/addPersonWang',person)
//方式二:借助mapActions:
...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
  1. 开启命名空间后,组件中调用commit
//方式一:自己直接commit
this.$store.commit('personAbout/ADD_PERSON',person)
//方式二:借助mapMutations:
...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'}),

7.7 终极案例

在这里插入图片描述

  • 文件分布

在这里插入图片描述

// src/store/index.js
// 创建 store

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

// 引入模块化 vuex 组件
import countOptions from './count'
import personOptions from './person'

Vue.use(Vuex)

export default new Vuex.Store({
    modules: {
        countOptions,
        personOptions
    }
})
import Vue from 'vue'
import App from './App.vue'

// 引入 store
import store from './store'

Vue.config.productionTip = false

new Vue ({
    el: '#app',
    render: h => h(App),
    store,
    beforeCreate() {
        Vue.prototype.$bus = this
    }
})
// count.js

export default {
    namespaced: true,
    actions: {
        incrementOdd(context, value) {
            if(context.state.sum % 2)
                context.commit('INCREMENT', value)
        },
        incrementWait(context, value) {
            setTimeout(() => {
                context.commit('INCREMENT', value)
            }, 500)
        }
    },
    mutations: {
        INCREMENT(state, value) {
            state.sum += value
        },
        DECREMENT(state, value) {
            state.sum -= value
        }
    },
    state: {
        sum: 0,
        school: '尚硅谷',
        subject: '前端'
    },
    getters: {
        bigSum(state){
            return state.sum * 10
        }
    }
}
// person.js

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

export default {
    namespaced: true,
    actions: {
        // 从远程服务器获取人的姓名并添加
        addPersonFromServer(context) {
            axios.get('https://api.uixsj.cn/hitokoto/get?type=social').then(
                response => {
                    context.commit('ADD_PERSON', response.data)
                },
                error => {
                    alert(error.message)
                }
            )
        }
    },
    mutations: {
        ADD_PERSON(state, value) {
            const personObj = {id: nanoid(), name: value}
            state.personList.unshift(personObj)
        }
    },
    state: {
        personList: [
            {id: '001', name: '张三'}
        ]
    },
    getters: {}
}
<template>
	<div>
		<h1>当前求和为:{{sum}}</h1>
		<h3>当前求和放大10倍为:{{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="increment(n)">+</button>
		<button @click="decrement(n)">-</button>
		<button @click="incrementOdd(n)">当前求和为奇数再加</button>
		<button @click="incrementWait(n)">等一等再加</button>
		<h1 style="color: red">Person 组件当前人数为:{{personList.length}}</h1>
	</div>
</template>

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

	export default {
		name:'Count',
		data() {
			return {
				n: 1
			}
		},
		computed: {
			...mapState('countOptions', ['sum', 'school', 'subject']),
			...mapState('personOptions', ['personList']),
			...mapGetters('countOptions', ['bigSum'])
		},
		methods: {
			...mapMutations('countOptions', {increment:'INCREMENT',decrement:'DECREMENT'}),
			...mapActions('countOptions', ['incrementOdd', 'incrementWait'])
		}
	}
</script>

<style scoped>
	button {
		margin-left: 5px;
	}
</style>
<template>
    <div>
        <h1>人员列表</h1>
        <input type="text" placeholder="请输入名字" v-model="name">
        <button @click="addPerson(name)">添加</button>
        <!-- 从远程服务器取数据添加 -->
        <button @click="addPersonFromServer(name)">从远程服务器添加人员</button>
        <ul>
            <li v-for="person in personList" :key="person.id">{{person.name}}</li>
        </ul>
        <h1 style="color: red">Count 组件当前求和为:{{sum}}</h1>
    </div>
</template>

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

    export default {
        name: 'Person',
        computed: {
            ...mapState('personOptions', ['personList']),
            ...mapState('countOptions', ['sum'])
        },
        data() {
            return {
                name: ''
            }
        },
        methods: {
            ...mapMutations('personOptions', {addPerson: 'ADD_PERSON'}),
            // 从远程服务器添加人员
            ...mapActions('personOptions', ['addPersonFromServer'])
        }
    }
</script>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值