您现在的位置是:首页 >技术交流 >12-Vue技术栈之Vuex的使用网站首页技术交流
12-Vue技术栈之Vuex的使用
简介12-Vue技术栈之Vuex的使用
目录
1、理解 vuex
1.1 vuex 是什么
- 概念:专门在 Vue 中实现集中式
状态(数据)
管理的一个 Vue 插件,对 vue 应用中多个组件的共享
状态进行集中式的管理(读/写),也是一种组件间通信的方式
,且适用于任意组件间通信
。 - Github 地址: https://github.com/vuejs/vuex
1.2 什么时候使用 Vue
- 多个组件依赖于同一状态
- 来自不同组件的行为需要变更同一状态
简单来说就是
多个组件需要共享数据时
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 配置文件
- 创建文件:
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
})
- 在
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工作原理图:
- 初始化数据、配置
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,
})
-
组件中读取vuex中的数据:
$store.state.sum
-
组件中修改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 = false
new 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的使用
-
概念:当state中的数据需要经过加工后再使用时,可以使用getters加工。
-
在
store.js
中追加getters
配置...... const getters = { bigSum(state){ return state.sum * 10 //靠返回值 } } //创建并暴露store export default new Vuex.Store({ ...... getters })
-
组件中读取数据:
$store.getters.bigSum
5、四个map方法的使用
- mapState方法:用于帮助我们映射
state
中的数据为计算属性
computed: {
//借助mapState生成计算属性:sum、school、subject(对象写法)
...mapState({sum:'sum',school:'school',subject:'subject'}),
//借助mapState生成计算属性:sum、school、subject(数组写法)
...mapState(['sum','school','subject']),
},
- mapGetters方法:用于帮助我们映射
getters
中的数据为计算属性
computed: {
//借助mapGetters生成计算属性:bigSum(对象写法)
...mapGetters({bigSum:'bigSum'}),
//借助mapGetters生成计算属性:bigSum(数组写法)
...mapGetters(['bigSum'])
},
- mapActions方法:用于帮助我们生成与
actions
对话的方法,即:包含$store.dispatch(xxx)
的函数
methods:{
//靠mapActions生成:incrementOdd、incrementWait(对象形式)
...mapActions({incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
//靠mapActions生成:incrementOdd、incrementWait(数组形式)
...mapActions(['jiaOdd','jiaWait'])
}
- mapMutations方法:用于帮助我们生成与
mutations
对话的方法,即:包含$store.commit(xxx)
的函数
methods:{
//靠mapActions生成:increment、decrement(对象形式)
...mapMutations({increment:'JIA',decrement:'JIAN'}),
//靠mapMutations生成:JIA、JIAN(对象形式)
...mapMutations(['JIA','JIAN']),
}
备注:在四个方法使用时,若需要传递参数需要:在模板中绑定事件时传递好参数,否则参数是事件对象。
5.1 求和案例
- 我们将上面的求和案例利用这些方法进行改进一下:
- 其他文件照常,只改变store里面的
index.js
和Count组件
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 = false
new 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.persons
return 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
}
}
}
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。