简介
专门为Vue单页应用实现集中式状态(数据)管理的一个Vue插件
作用
对Vue应用中多个组件的共享状态(数据)进行集中式管理(读/写),也是一种组件间的通信方式,适用于任意组件间的通信
什么时候使用
多个组件共享同一个状态(数据),最起码都要有2个组件以上共有这个数据才使用Vuex
使用
安装
npm i vuex
引入并使用
import Vuex from 'vuex'
Vue.use(Vuex)
执行上面代码后,vm、vc对象上就能挂载一个属性 $store
创建 store 对象
在 src/plugins/ 下创建 store.js 文件,代码如下
import Vue from 'vue'
import Vuex from '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 中配置 store 项(关联)
import Vue from 'vue'
import store from '@/plugins/store'
new Vue({
store,
el: '#app'
})
安装完成后,所有vm、vc都会新增一个共有属性:store`添加
工作原理
从图上可以看出,Vuex最核心、最必备的3大组成部分分别是:Actions、Mutations、State
Actions负责与后端进行通信获取数据和业务处理
Mutations负责简单的数据更改,也是VueDevTools工具监视的位置
必备配置项(3种)
Actions
与 Mutation 类似,唯一区别是:
mutation 是同步函数,唯一修改 state 状态值的方法
action 是异步函数,但不能修改 state 状态值
定义函数
// 定义的函数接受2个参数:
1、context
2、传参
actions: {
incrementAsync (context, params) {
setTimeout(() => {
context.commit('increment', params)
}, 1000)
}
}
组件中使用
// 第一种方式
this.$store.dispatch('incrementAsync', 12);
// 第二种方式 (当成方法使用,更加简单方便)
1、从 vuex 中按需导入 mapMutation 辅助函数
import { mapActions } from vuex
2、将导入的 action 函数,映射为当前组件的methods函数
methods: {
...mapActions(['incrementAsync'])
}
3、调用
this.incrementAsync(参数);
模块化时,该如何使用呢?
// 第一种方式
this.$store.dispatch('base/incrementAsync', 12);
// 第二种方式
1、从 vuex 中按需导入 mapMutation 辅助函数
import {mapActions} from vuex
2、将导入的 action 函数,映射为当前组件的methods函数
methods: {
...mapActions(['base/incrementAsync']),
...mapActions('base', ['incrementAsync'])
}
3、调用
this['base/incrementAsync'](4)
this.incrementAsync(13)
Mutations
vuex 不允许直接修改 state 中数据,必须使用 Mutation 来修改
Mutation 中定义的函数必须是同步函数,不能是异步的
声明函数
// 定义的函数接受2个参数:
1、state
2、传参
mutations: {
increment (state, params) {
// 变更状态
state.count++
}
}
组件中使用
// 第一种方式
this.$store.commit('increment', 12);
// 第二种方式 (当成方法使用,更加简单方便)
1、从 vuex 中按需导入 mapMutation 辅助函数
import {mapMutations} from vuex
2、将导入的 mutation 函数,映射为当前组件的methods函数
methods: {
...mapMutations(['increment'])
}
3、调用
this.increment(参数);
模块化时,该如何使用呢?
// 第一种方式
this.$store.commit('base/increment', 12);
// 第二种方式
1、从 vuex 中按需导入 mapMutation 辅助函数
import {mapMutations} from vuex
2、将导入的 mutation 函数,映射为当前组件的methods函数
methods: {
...mapMutations(['base/increment']),
...mapMutations('base', ['increment'])
}
3、调用
this['base/increment'](4)
this.increment(13)
State
用于存储所有共享数据
组件使用时,要在 computed 声明
与data一样,都实现了数据代理,都是响应式的数据(数据变,依赖数据的页面也发生变化)
第一种、直接访问(不推荐)
// 非模块
this.$store.state.全局数据名称
// 模块
this.$store.state.模块名.全局数据名称
第二种、使用辅助函数(推荐)
1、从 vuex 按需导入 mapState 辅助函数
import {mapState} from 'vuex'
2、将当前组件需要的全局数据,映射为当前组件的computed计算属性
// 非模块写法
computed: {
...mapState(['count'])
}
或者
computed: {
...mapState({
count: state => state.count, // 第一种
count: 'count' // 第二种
})
}
// 模块
computed: {
...mapState({
count: state => state.模块名.count
})
}
-注意-
官方推荐我们将vuex的state属性绑定到computed,为何不可以绑定到data里面呢?
错觉:data中的内容只会在 created 钩子触发前初始化一次,之后需要通过js修改data里面的属性值,页面才响应式改变,那么把state里的数据绑定到data中,state中数据改变,data中对应的数据也会跟着改变不对吗?
回答:上面说法有一半对,一半错。到底错在哪呢?
错在:如果state里面的状态值类型是数值、布尔、字符串基础数据类型的话,那么赋值就是值传递,如下
let a = 12;
let b = a;
a = 20;
alert(a) //20
alert(b) //12
这样你明白了吧,如果把state中状态传递给data,那么当state中状态改变后,因为不是引用传递,data中对应属性值不会改变
总结:
data是在create钩子触发前初始化一次
data中由于没有依赖跟踪,所以必须引用传递才可以做到响应式
选配配置项
Getters
用于对 state 中数据进行加工,不会对 state 中原数据有任何修改
类似于 computed 计算属性,要复用可以使用 getters
state 中数据发生变化, getter 中数据也发生变化 —— 响应式
组件使用时,要在 computed 声明
配置
在Store对象创建时添加配置项getters
const getters = {
bigSum(state){
return state.sum*100-20;
}
}
export default new Vuex.Store({
......
getters
})
使用
// 第一种方式 (不推荐)
this.$store.getters.名称
// 第二种方式 (优化写法,推荐)
1、从 vuex 中按需导入 mapGetters 辅助函数
import {mapGetters} from vuex
2、将导入的 getter 函数,映射为当前组件的 computed 计算属性
computed: {
...mapGetters(['bigSum'])
}
3、模板中使用
<div>{{ bigSum }}</div>
Modules 模块化 (重点)
Vue单页应用必备的技术,为防止代码过于冗重,我们必须使用模块化思想,把代码分门别类拆分,便于维护
拆分模块
根据业务功能拆分模块,模块如下定义:
// 创建文件定义模块a
export default {
namespaced: true, // 必须开启模块命名空间
state: {
name: 'xxx',
title: 'ssss',
sum: 0
},
actions: {
getUpdata(context, value){}
},
mutations: {
updateSum(state, value){}
},
getters: {
bigSum(state){
return state.name*10;
}
}
}
配置模块
// 导入模块
import xxx from './a'
import aaa from './b'
export default new Vuex.Store({
modules: {
xxx,
aaa
}
})
使用
非辅助函数使用
// state
{{$store.state.模块名.属性名}}
this.$store.state.模块命名空间名.属性名
// getters
{{$store.getters['模块名/属性名']}}
this.$store.getters['模块名/属性名'] // 只能这样写
// mutations
this.$store.commit('模块命名空间名/函数名', 传参)
// actions
this.$store.dispatch('模块命名空间名/函数名', 传参)
辅助函数使用
computed: {
// state
...mapState(命名空间名,{使用名1:'xxx', 使用名2: 'aaa'})
...mapState(命名空间名,['xxx', 'aaa'])
// getters
...mapGetters(命名空间名,{使用名1:'xxx', 使用名2: 'aaa'})
...mapGetters(命名空间名,['xxx', 'aaa'])
// mutations
...mapMutations(命名空间名,{函数名1:'xxx', 函数名1: 'aaa'})
...mapMutations(命名空间名,['xxx', 'aaa'])
// actions
...mapActions(命名空间名,{函数名1:'xxx', 函数名1: 'aaa'})
...mapActions(命名空间名,['xxx', 'aaa'])
}
代码生成器(简化代码)
Vuex为了开发者更加方便使用Vuex中存储的状态(数据),给开发者提供了4个辅助函数,用于生成计算属性代码
mapState
借助 mapState 函数生成计算属性代码,用于帮我们映射 State 中的数据为计算属性
对象写法
import {mapState} from 'vuex'
export default {
name: 'book',
data(){
return {}
},
computed: {
...mapState({he: 'sum', bigSum: 'bigSum'})
}
}
// 模板中使用
<p>{{he}}</p>
这种写法最大的好处是,当你遇到data或computed中原本的变量名与vuex中状态的变量发生命名冲突时,可以使用此写法更换一个名称,避免命名冲突
数组写法
确定不发生变量命名冲突时,直接使用原本vuex中数据名称,就可以使用此写法
import {mapState} from 'vuex'
export default {
name: 'book',
data(){
return {}
},
computed: {
...mapState(['sum', 'bigSum'])
}
}
// 模板中使用
<p>{{sum}}</p>
mapGetters
借助 mapGetters 函数生成计算属性代码,用于帮我们映射 Getters 中的数据为计算属性
和 mapState 用法一样
对象写法
import {mapGetters} from 'vuex'
export default {
name: 'book',
data(){
return {}
},
computed: {
...mapGetters({he: 'sum', bigSum: 'bigSum'})
}
}
// 模板中使用
<p>{{he}}</p>
数组写法
import {mapGetters} from 'vuex'
export default {
name: 'book',
data(){
return {}
},
computed: {
...mapGetters(['sum', 'bigSum'])
}
}
// 模板中使用
<p>{{sum}}</p>
mapMutations
借助 mapMutations 生成对应的方法代码,用于帮我们生成与Mutations 对话的方法,即:包含$store.commit()函数
使用mapMutations前
export default {
name: 'book',
data(){
return {
n: 1
}
},
methods: {
updateCommonData(){
this.$store.commit('goTest', this.n);
}
}
}
每次调用Mutations中的方法都要写一段冗余的代码this.$store.commit('xxxx', 参数),Vuex为了解决这个问题,提供给开发者一个辅助生成代码函数mapMutations
使用mapMutations后
export default {
name: 'book',
data(){
return {
n: 1
}
},
methods: {
...mapMutations({updataCommonData: 'goTest'})
}
}
...mapMutations({updataCommonData: 'goTest'})最终生成代码如何?
updateCommonData(value){
this.$store.commit('goTest', value);
}
对象写法
唯一好处就是能够更改方法名称,解决命名冲突
export default {
name: 'book',
data(){
return {
n: 1
}
},
methods: {
...mapMutations({updataCommonData: 'goTest'})
}
}
数组写法
唯一好处就是够简单
export default {
name: 'book',
data(){
return {
n: 1
}
},
methods: {
...mapMutations(['updataCommonData'])
}
}
mapActions
借助 mapActions 生成对应的方法代码,用于帮我们生成与mapActions 对话的方法,即:包含$store.dispatch()函数
对象写法
唯一好处就是能够更改方法名称,解决命名冲突
export default {
name: 'book',
data(){
return {
n: 1
}
},
methods: {
...mapActions({updataCommonData: 'goTest'})
}
}
数组写法
唯一好处就是够简单
export default {
name: 'book',
data(){
return {
n: 1
}
},
methods: {
...mapActions(['updataCommonData'])
}
}