目录
- 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 是什么
- 概念:专门在 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 = 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的使用
-
概念:当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 = 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}}
}