Vuex
1. 理解 Vuex
1.1 vuex是什么
专门在 Vue中实现集中式状态(数据)管理的一个 vue 插件,对 vue 应用中多个组件的共享状态进行集中式的管理(读/写),也是一种组件间的通讯方式,且适用于任意组件间的通讯。
多组件共享数据
1.2 什么情况下使用 vuex
- 多个组件依赖于同一状态
- 来自不同组件的行为需要变更同一状态
1.3 Vuex 工作原理
2. 安装及其使用
2.1安装和初始化工作
-
下载
npm i vuex@2.3.1 --save
-
使用插件
Vue.use(Vuex)
new 实例对象时可以加入 store 的配置项
// 引入 vuex import Vuex from 'vuex' // 使用插件 Vue.use(Vuex)
-
创建
store
在 src 目录下创建 store 文件夹,放一个 index.js 文件
src/store/index.js
// 该文件用于创建 Vuex 中最为核心的 store
// 引入 Vuex
import Vuex from 'vuex'
// 准备 actions
// 用于响应组件的动作
const actions = {}
// 准备 mutations
// 用于操作数据
const mutations = {}
// 准备 state
// 用于储存数据
const state = {}
// 创建并导出 store
export default new Vuex.Store({
actions,
mutations,
state
})
main.js 中引入 store并配置
import Vue from 'vue'
import App from './App.vue'
// 引入 vuex
import Vuex from 'vuex'
// 引入 store
import store from './store'
Vue.config.productionTip = false
// 使用插件
Vue.use(Vuex)
new Vue({
render: h => h(App),
store
}).$mount('#app')
报错Uncaught Error: [vuex] must call Vue.use(Vuex) before creating a store instance.
Vue.use(Vuex) 需要在创建 store 之前执行
不能通过调整 import 语句的顺序来解决这个问题,因为vue脚手架会将import语句提升到文件最前面执行,解决:将 Vue.use(Vuex) 这行代码放在 src/store/index.js
的文件中靠前面的位置。
// 该文件用于创建 Vuex 中最为核心的 store
// 引入 Vuex
import Vuex from 'vuex'
import Vue from 'vue'
// 使用插件
Vue.use(Vuex)
//..code
2.2 使用
-
sum 放在
state
中const state = { // 当前的和 sum: 0 }
-
在
actions
中添加 add 方法- add 方法包含两个参数 context ,value
context
mini版的 store,是一个上下文,包含commit,可以调用向 mutations提交处理value
是传入的值- 最好将 actions中的方法名写成小写,将 mutations 中的方法名写成大写,便于区分
const actions = { add(context,value){ console.log('add',context,value) context.commit('add' , value) } }
-
在
mutations
中添加 ADD方法参数 state 中存放着数据,和一些方法
const mutations = { ADD(state,value){ console.log(state,value) state.sum += value } }
-
在页面中使用插值语法展示state中的数据
<h1>当前求和 : {{$store.state.sum}}</h1>
-
完善 methods ,actions 和 mutations
methods: { increment(){ this.$store.dispatch('add' , this.n) }, decrement(){ this.$store.dispatch('sub' , this.n) }, incrementOdd(){ this.$store.dispatch('addOdd' , this.n) }, incrementWait(){ this.$store.dispatch('addWait' , this.n) } }
const actions = { add(context,value){ console.log('actions 调用了 add') context.commit('ADD' , value) }, sub(context,value){ console.log('actions 调用了 sub') context.commit('SUB' , value) }, addOdd(context,value){ console.log('actions 调用了 addOdd') if(context.state.sum %2 != 0){ context.commit('ADD' , value) } }, addWait(context,value){ console.log('actions 调用了 addWait') setTimeout(() => { context.commit('ADD' , value) },3000) }, } const mutations = { ADD(state,value){ console.log('mutations 调用了 ADD') state.sum += value }, SUB(context,value){ console.log('mutations 调用了 SUB') state.sum -= value } }
-
由于 actions 中只有一个 commit 逻辑,可以简化
将 dispatch 改成 commit,并且将调用的小写的actions中的方法改成大写的 mutations中的方法
increment(){ this.$store.commit('ADD' , this.n) }, decrement(){ this.$store.commit('SUB' , this.n) },
3. Vuex 开发者工具
4. store 的一些配置项
4.1 getters
在 state 中的数据需要经过加工之后再使用,可以使用 getters 加工
组件中通过 $store.getters.xxx
访问
类似于计算属性,但是计算属性只能在当前组件中访问到
state
==> getters
data
==> computed
在 src/store/index.js
中追加 getters 配置:
// 准备 getters 用于将 state 中的数据加工
const getters = {
bigSum(state){
return state.sum * 10
}
}
export default new Vuex.Store({
// ...
getters
})
4.2 mapState 和 mapGetters
-
mapState
帮助我们映射state
中的数据为计算属性 -
mapGetters
帮助我们映射getters
中的数据为计算属性
原始的组件页面结构,太臃肿
<h1>当前求和 : {{$store.state.sum}}</h1>
<h3>我在{{$store.state.school}},学习{{$store.state.subject}}</h3>
-
在 script 中引入 vuex
import {mapState} from 'vuex'
-
在computed 中生成计算属性的代码
computed:{ // 借助 mapState 生成计算属性,从 state 中读取数据 (对象写法) ...mapState({ sum: 'sum', school: 'school', subject: 'subject' }), // 数组写法 ...mapState(['sum','school','subject']), //相当于 //sum(){ // return this.$store.state.sum //} },
-
页面结构,更加简洁
<h1>当前求和 : {{sum}}</h1> <h3>我在{{school}},学习{{subject}}</h3
-
mapGetters
<h1> * 10 过后 : {{$store.getters.bigSum}}</h1> <h1> * 10 过后 : {{bigSum}}</h1>
import {mapState , mapGetters} from 'vuex' // 借助 mapGetters 生成计算属性,从 getters 中读取数据 (对象写法) ...mapGetters({ bigSum: 'bigSum', }), // 数组写法 ...mapGetters(['bigSum']),
4.3 mapActions 与 mapMutations
-
用于帮助我们生成与
mutations
对话的方法,包含$store.commit(xxx)
的函数-
原生手写的代码和使用 mapMutations
// increment(){ // this.$store.commit('ADD' , this.n) // }, // decrement(){ // this.$store.commit('SUB' , this.n) // }, ...mapMutations({ increment: 'ADD', decrement: 'SUB' }),
-
注意:使用 mapMutations 时,相当于
increment(e){ this.$store.commit('ADD' , e) }
所以 n 需要我们在触发事件函数的时候手动传参。
<button @*click*="increment(n)">+</button>
-
-
用于帮助我们生成与 mutations 对话的方法,包含
this.$store.dispatch(xxx)
的函数...mapActions({ incrementOdd: 'addOdd', incrementWait: 'addWait' })
5. 多组件共享数据
添加一个 Person.vue 组件来实现与 Count.vue 组件通过 vuex 来共享
6. 模块化 + 命名空间
-
目的:让代码更好维护,让多种数据分类更多明确。
-
修改
store/index.js
import countOptions from './count' import personOptions from './person' export default new Vuex.Store({ modules: { countAbout: countOptions, personAbout: personOptions } })
count.js
// 求和相关配置 const countOptions = { namespaced: true,//开启命名空间 actions: { addOdd(context,value){ console.log('actions 调用了 addOdd') if(context.state.sum %2 != 0){ context.commit('ADD' , value) } }, addWait(context,value){ console.log('actions 调用了 addWait') setTimeout(() => { context.commit('ADD' , value) },3000) }, }, mutations: { ADD(state,value){ console.log('mutations 调用了 ADD') state.sum += value }, SUB(state,value){ console.log('mutations 调用了 SUB') state.sum -= value }, }, state: { // 当前的和 sum: 0, school: '尚硅谷', subject: '前端', }, getters: { bigSum(state){ return state.sum * 10 } } } export default countOptions
person.js
// 人员相关配置 import {nanoid} from 'nanoid' const personOptions = { namespaced: true,//开启命名空间 actions: { addPersonWang(context,value){ if(value.name.indexOf('王') === 0){ context.commit('ADD_PERSON', value) }else{ alert('添加的人员姓必须是王!') } }, addPersonServer(context){ var ajax = new XMLHttpRequest() ajax.open('get','https://api.uixsj.cn/hitokoto/get?type=social') ajax.send() ajax.onreadystatechange = function () { if (ajax.readyState==4 &&ajax.status==200) { context.commit('ADD_PERSON', { id: nanoid(), name: ajax.responseText }) } } // axios.get('https://api.uixsj.cn/hitokoto/get?type=social').then( // response => { // context.commit('ADD_PERSON', { // id: nanoid(), // name: response.data // }) // }, // err => { // alert(err.message) // } // ) } }, mutations: { ADD_PERSON(state,value){ console.log('mutations 调用了 ADD_PERSON') state.personList.unshift(value) } }, state: { personList:[ {id: '001', name: '张三'} ] }, getters: { // state 是相对于当前的配置组件 firstPersonName(state){ return state.personList[0].name } } } export default personOptions
-
开启命名空间后,组件中读取 state 数据
this.$store.state.personAbout.personList ...mapState('countAbout',['sum','school','subject']),
-
开启命名空间后,组件中读取 getters 数据
this.$store.getters['personAbout/firstPersonName'] ...mapGetters('countAbout',['bigSum']),
-
开启命名空间后,组件中调用 dispatch
this.$store.dispatch('personAbout/addPersonWang', personObj) ...mapActions('countAbout',{ incrementOdd: 'addOdd', incrementWait: 'addWait' })
-
开启命名空间后,组件中调用 commit
this.$store.commit('personAbout/ADD_PERSON', personObj) ...mapMutations('countAbout',{ increment: 'ADD', decrement: 'SUB' }),