:山不向我走来,我便向它走去
更多Vue知识请点击——Vue.js
概念:专门在Vue中实现集中式状态(数据)管理的一个Vue插件,对Vue应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间通信的方式,且适用于任意组件间通信。
其实就是——共享
安装:npm i vuex@3
注意Vue2一定要安装vuex3,如果是vue3可以直接npm i vuex
安装的是vuex4,可以去package.json
文件里看下vue的版本是啥,千万别输错了,不然会陷入痛苦的报错……
创建文件路径:src/store/index.js
在此文件中引入插件并使用vuex插件,使用vuex插件必须在引入store之前,如果在main.js中引入和使用vuex的话,由于js文件里所有的import语句都会提升到最开始执行,所以会报错。总结:引入store必须在Vue.use(Vuex)之后
//该文件用于创建Vuex中最为核心的store
//引入Vue
import Vue from 'vue';
//引入Vuex
import Vuex from 'vuex';//引入插件并使用插件
Vue.use(Vuex); //使用插件后就可以在vm,vc里使用store配置项
//准备actions,用于响应组件中的动作
const actions = {};
//准备mutations,用于操作数据(state)
const mutations = {};
//准备state,用于存储数据
const state = {};
//创建store
const store = new Vuex.Store({
actions: actions,
mutations, //简写
state //简写
});
//导出store
export default store;
JS执行的时候会把import提升到顶部,与摆放顺序无关,如果放在main.js里 import store from './store'
无论放到哪里都会比Vue.use(Vuex)
先执行,要想把 Vue.use(Vuex)
要放到实例化之前只有放进index.js
//js文件里所有的import语句都会提升到最开始执行
// 引入Vue
import Vue from 'vue';
// 引入App
import App from './App.vue';
Vue.config.productionTip = false;
//引入store
import store from './store/index.js';
// 创建一个Vue实例
new Vue({
el: '#app',
store: store, //或者直接写store
render: h => h(App),
beforeCreate() {
Vue.prototype.$bus = this; //创建全局事件总线
}
});
这样的话,vm和所有vc就都能碰到$store
了
这个工作流程用一个通俗的例子来解释:VC就是顾客,actions就是服务员,mutations就是厨师,state就是菜的原料。顾客(VC)来到店里,先告诉(dispatch)服务员(actions)要吃啥菜,然后服务员把菜单给(commit)厨师(mutations),完了之后厨师对菜的原料(state)来个加工(mutate),最后再给(render)顾客(VC)。
使用vuex实现求和案例:
下拉框能选择要操作的数字,分别用总数对这个数字进行加减等操作。
下面的操作不懂的时候可以翻到前面的原理图,结合图更容易理解哦~
1、首先数据应该放在state里面,所以把求和数据给 vuex的state对象
const state = {
sum: 0, //初始化数据
};
2、页面上插值语法就该用
<h1>当前求和为:{{ $store.state.sum }}</h1>
3、在组件中的回调就可以用dispatch
发给actions
再commit
给mutations
。注意如果没有业务逻辑,直接commit
可以跳过actions
直接给mutations
(也就是上面原理图粉红色那条线)。
跳过actions
然后commit给mutations
。dispatch发给actions
再commit给mutations
代码文件如下:
当前求和为:{{ $store.state.sum }}
//该文件用于创建vuex中最核心的store
// 引入Vue
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex插件
Vue.use(Vuex)
//准备actions:用于响应组件中的动作
const actions = {
//是奇数就加
//第一个参数是浓缩版的$store,方便你在这里调用commit把东西给mutations
//第二个参数是传过来的数据
oddAdd(context, value) {
// console.log('actions中的oddadd被调用了')
if (context.state.sum % 2) {
context.commit('JIA', value)
}
},
//等一等再加
waitAdd(context, value) {
// console.log('actions中的waitadd被调用了')
setTimeout(() => {
context.commit('JIA', value)
}, 1000)
},
}
//准备mutations:用于操作数据(state)
const mutations = {
//加
//第一个参数是state对象,第二个参数是传过来的数据
JIA(state, value) {
// console.log('mutations中的JIA被调用了', state, value)
state.sum += value
},
//减
JIAN(state, value) {
// console.log('mutations中的JIAN被调用了', state, value)
state.sum -= value
},
}
//准备state:用于存储数据
const state = {
sum: 0, //初始化数据,当前的和
}
//创建store并暴露store
export default new Vuex.Store({
actions,
mutations,
state,
})
1、概念:当state中的数据需要经过加工后再使用时,可以使用getters加工,类似Vue中的计算属性computed。
2、使用:在store\index.js
中追加getters
配置,写函数,页面读的时候读的是返回值,这点其实也和计算属性很像。
......
//准备 getters ---用于将state中的数据进行加工
const getters = {
bigSum(state){
return state.sum * 10
}
}
//创建并暴露store
export default new Vuex.Store({
......
getters
})
3、组件中读取数据:$store.getters.bigSum
<h1>当前求和放大十倍后为:{{ $store.getters.bigSum }}</h1>
4、其实state
就类似于data
,getters
就类似computed
我们之前要往页面上放state中的数据,还得$store.state.xxx
,或者 $store.getters.xxx
<h1>当前求和为:{{ $store.state.sum }}</h1>
<h1>当前求和放大十倍后为:{{ $store.getters.bigSum }}</h1>
<h1>我在{{ $store.state.school }}学习{{ $store.state.subject }}</h1>
真的是非常麻烦啊,想简单点写,写成下面这样:
<h1>当前求和为:{{ sum }}</h1>
<h1>当前求和放大十倍后为:{{ bigSum }}</h1>
<h1>我在{{ school }}学习{{ subject }}</h1>
computed: {
//靠程序员亲自写计算属性来实现state插值语法编码方便
sum() {
return this.$store.state.sum;
},
school() {
return this.$store.state.school;
},
subject() {
return this.$store.state.subject;
},
bigSum() {
return this.$store.getters.bigSum;
}
},
但是实际上亲自写计算属性来实现state插值语法computed这些东西复用性很差,vuex给我们提供了一个mapState
和mapGetters
方法:用于帮助我们把state
和getters
中的数据映射为计算属性。
先引入这两个map方法: import {mapState,mapGetters} from 'vuex'
mapstate
与mapGetters
用到computed
里。
然后写法如下:
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]),
},
注意:对象写法可以任意起名,键对应计算属性方法名
,值对应state中的数据名
,如果方法名和数据名一样
,就可以用数组形式简写
之前我们这个要从组件直接用commit传数据给mutations,或者用dispatch传给actions,都需要在methods里配置:
methods: {
//程序员费老大劲写的传mutations代码
add() {
//如果没有业务逻辑,直接commit给mutations
this.$store.commit('JIA', this.addnum);
},
subtract() {
this.$store.commit('JIAN', this.addnum);
},
//程序员费老大劲写的传actions代码
oddAdd() {
//如果有业务逻辑,先dispatch给actions,再commit给mutations
this.$store.dispatch('oddAdd', this.addnum);
},
waitAdd() {
this.$store.dispatch('waitAdd', this.addnum);
},
},
Vuex给我们提供了mapMutations
和mapActions
方法
mapMutations
方法:用于帮助我们生成与mutations
对话的方法,即:包含$store.commit(xxx)
的函数。
mapActions
方法:用于帮助我们生成与actions
对话的方法,即:包含$store.dispatch(xxx)
的函数。
先引入这两个map方法: import {mapMutations,mapActions} from 'vuex'
mapMutations
与mapActions
用到methods
里。
写法如下:
methods: {
//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(对象写法)
...mapMutations({ add: 'JIA', subtract: 'JIAN' }),
//借助mapMutations生成对应的方法,方法中会调用commit去联系mutations(数组写法)
// ...mapMutations(['JIA','JIAN']),
//借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(对象写法)
...mapActions({ oddAdd: 'oddAdd', waitAdd: 'waitAdd' }),
//借助mapActions生成对应的方法,方法中会调用dispatch去联系actions(数组写法)
// ...mapMutations(['oddAdd','waitAdd']),
},
注意:mapActions
与mapMutations
使用时,其实创建的程序员费老大劲写的代码是不一样的,这两个玩意儿创建的东西是不带数据的,它创建的是这个:
add(value) {
this.$store.commit('JIA', value);
},
若需要传递参数value
,需要:在模板中绑定事件时传递好参数,否则参数value
是事件对象。
<button @click="JIA(addnum)">+</button>
<button @click="JIAN(addnum)">-</button>
<button @click="oddAdd(addnum)">当前求和为奇数再加</button>
<button @click="waitAdd(addnum)">等1秒再加</button>
注意:对象写法中,键是方法名
,值是传给mutations
或者actions
的方法名
,如果方法名和传的方法名一样可以简写为数组形式
//该文件用于创建vuex中最核心的store
// 引入Vue
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex插件
Vue.use(Vuex)
//准备actions:用于响应组件中的动作
const actions = {
//是奇数就加
oddAdd(context, value) {
// console.log('actions中的oddadd被调用了')
if (context.state.sum % 2) {
context.commit('JIA', value)
}
},
//等一等再加
waitAdd(context, value) {
// console.log('actions中的waitadd被调用了')
setTimeout(() => {
context.commit('JIA', value)
}, 1000)
},
}
//准备mutations:用于操作数据(state)
const mutations = {
//加
JIA(state, value) {
// console.log('mutations中的JIA被调用了', state, value)
state.sum += value
},
//减
JIAN(state, value) {
// console.log('mutations中的JIAN被调用了', state, value)
state.sum -= value
},
ADD_PERSON(state, value) {
state.personList.unshift(value)
},
}
//准备state:用于存储数据
const state = {
sum: 0, //初始化数据,当前的和
school: 'potato',
subject: '前端',
personList: [{ id: '001', name: '张三' }],
}
//准备getters:用于将state中的数据进行加工
const getters = {
bigSum(state) {
return state.sum * 10
},
}
//创建store并暴露store
export default new Vuex.Store({
actions,
mutations,
state,
getters,
})
当前求和为:{{ sum }}
当前求和放大10倍:{{ bigSum }}
我在{{ school }}学习{{ subject }}
下方组件的总人数是:{{ personList.length }}
人员列表
- {{ p.name }}
如果我们写的state,actions什么的是服务于多个种类的,比如有管加法的,有管人员的,这样放到一起很乱,所以可以把它们拆开
作用:让代码更好维护,让多种数据分类更加明确。
可以都写到index.js里,也可以每个命名空间分别拆成多个js文件
const countAbout = {
namespaced:true,//开启命名空间
state:{x:1},
mutations: { ... },
actions: { ... },
getters: {...}
}
}
const personAbout = {
namespaced:true,//开启命名空间
state:{ ... },
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
countAbout,
personAbout
}
})
(1)开启命名空间后,组件中读取state数据
//方式一:自己直接读取
this.$store.state.personAbout.list
//方式二:借助mapState读取:
...mapState('countAbout',['sum','school','subject']),
// 前边加个参数,意思是读取countAbout 里面的 sum,school.....
(2)开启命名空间后,组件中读取getters数据
//方式一:自己直接读取
this.$store.getters['personAbout/firstPersonName'] //注意这里的写法
//方式二:借助mapGetters读取:
...mapGetters('countAbout',['bigSum'])
(3)开启命名空间后,组件中调用dispatch
如果不写namespaced则直接写addPersonWang
就可以,但是开启了命名空间,必须要加上这个名字在前边,否则会报[vuex] unknown action type: addPersonWang
的错误,而且前边这个名字必须和Vuex.Store({})
配置项中的名字一致。
//方式一:自己直接dispatch
this.$store.dispatch('personAbout/addPersonWang',person)
//方式二:借助mapActions:
...mapActions('countAbout',{incrementOdd:'jiaOdd',incrementWait:'jiaWait'})
(4)开启命名空间后,组件中调用commit
//方式一:自己直接commit
this.$store.commit('personAbout/ADD_PERSON',person)
//方式二:借助mapMutations:
...mapMutations('countAbout',{increment:'JIA',decrement:'JIAN'}),
相信大家看完都有点懵了,可以直接看这个综合案例,几乎囊括了上文的所有知识点。
此案例的Count.vue和Person.vue里的计算属性和方法分别采用了两种不同的写法,Count.vue引入了mapXXX方法,Person.vue没有,可以对比一下他们的区别,你就知道mapXXX有多香。
建议大家复制下来自己运行看看,然后尝试自己凭印象再写一次。
案例目录结构如下:
// 引入Vue
import Vue from 'vue'
// 引入App
import App from './App.vue'
//引入插件
import vueResource from 'vue-resource'
//引入store
import store from './store'
// 关闭Vue的生产信息
Vue.config.productionTip = false
//使用插件
Vue.use(vueResource)
// 创建vm
new Vue({
el: '#app',
render: (h) => h(App),
store,
beforeCreate() {
Vue.prototype.$bus = this //安装全局事件总线
},
})
//该文件用于创建vuex中最核心的store
// 引入Vue
import Vue from 'vue'
//引入Vuex
import Vuex from 'vuex'
//应用Vuex插件
Vue.use(Vuex)
import countAbout from './Count'
import personAbout from './Person'
//创建store并暴露store
export default new Vuex.Store({
modules: {
countAbout,
personAbout,
},
})
export default {
namespaced: true,
state: {
sum: 0, //初始化数据
school: 'potato',
subject: '前端',
},
getters: {
bigSum(state) {
return state.sum * 10
},
},
mutations: {
JIA(state, value) {
state.sum += value
},
JIAN(state, value) {
state.sum -= value
},
},
actions: {
oddAdd(context, value) {
context.commit('JIA', value)
},
waitAdd(context, value) {
setTimeout(() => {
context.commit('JIA', value)
}, 1000)
},
},
}
当前求和为:{{ sum }}
当前求和放大10倍:{{ bigSum }}
我在{{ school }}学习{{ subject }}
下方组件的总人数是:{{ personList.length }}
import axios from 'axios'
import { nanoid } from 'nanoid'
export default {
namespaced: true,
state: {
personList: [{ id: '001', name: '张三' }],
},
mutations: {
ADD_PERSON(state, value) {
state.personList.unshift(value)
},
},
actions: {
addPersonWu(context, value) {
if (value.name.indexOf('吴') === 0) {
context.commit('ADD_PERSON', value)
} else {
alert('添加的人不姓吴!')
}
},
//发送ajax请求拿到名字
addPersonServer(context) {
axios.get('http://api.uixsj.cn/hitokoto/get?type=social').then(
(response) => {
context.commit('ADD_PERSON', { id: nanoid(), name: response.data })
},
(error) => {
console.log(error.message)
}
)
},
},
getters: {
firstPersonName(state) {
return state.personList[0].name
},
},
}
人员列表
第一个人的名字:{{ firstPersonName }}
- {{ p.name }}
Person里读sum:{{ add }}