依赖版本
- vue:2.6.14
- vuex:3.6.2
配置Vuex
- Vuex主要有5个配置部分,分别为
- state,状态,也就是要共享的数据
- mutations,同步修改state状态的函数
- actions,异步修改state状态的函数,其实就是异步结束后,调用mutations的函数,实现修改state数据
- getters,类似Vue组件的计算属性,可以对state状态数据进行统计、合并多个state为一个新的响应式变量
- modules,模块,Vuex支持定义模块,每个模块都有自己的state、mutations、actions、getters
创建Vuex实例(store\index.js)
// Vuex相关配置,注意:Vue2的Vuex版本是3版本
import Vue from 'vue';
import Vuex from 'vuex';
// 注册Vuex插件
Vue.use(Vuex);
// 创建仓库,管理数据
const store = new Vuex.Store({
// 严格模式,不能直接修改state的数据
// 默认为false,是可以直接修改的,但我们都要遵循单向数据流,不允许组件中直接修改状态,设置为true,如果直接修改数据,就会报错!!
strict: true,
// 状态,存放数据
state: {
},
// 同步修改state的函数,要求修改state,必须通过 mutation 函数
mutations: {
},
actions: {
// 异步修改数据,异步修改数据必须通过调用 mutation 函数,也就是在 mutation 上套了一层action
},
getters: {
// 类似计算属性,通过计算某个state,或处理多个state属性来得到一个结果
},
// 模块
modules: {
}
});
// 默认导出
export default store
使用Vuex(main.js)
import Vue from 'vue'
import App from './App.vue'
// 导入Vuex的仓库实例
// import store from './store/index.js' // 完整路径写法
// import store from './store/index'// js后缀可以省略
import store from './store' // 如果js文件的名称为index,index 也可以省略
Vue.config.productionTip = false
new Vue({
render: h => h(App),
// 挂载到 Vue 的实例上,以后就可以通过Vue的实例,通过 $store 获取到仓库的实例
store
}).$mount('#app')
开始使用
3种获取Vuex实例的方式
- 在state中,定义状态变量
const store = new Vuex.Store({
// 状态,存放数据
state: {
uname: '万大爷'
},
});
在Vue组件模板中
<div>
{{ $store.state.uname }}
</div>
在Vue组件的js脚本中
export default {
name: 'App',
components: {
HelloWorld
},
created() {
console.log(this.$store)
// 通过Vue实例上$store,适合在Vue组件的环境下使用
console.log(this.$store.state.uname)
}
在单独的js文件中
// 单独引入store,适合在非Vue组件环境下的时候使用,例如在 xx.js 中
import store from '@/store'
console.log(store.state.uname);
辅助函数
我们在vuex的state、mutations、actions、getters中,配置变量和函数,都在vuex的JS对象里面,想要获取某个变量或函数,需要一层层调用去取,非常麻烦。
所以vuex提供了4个辅助映射函数,将state、getters的状态变量,映射到组件的计算属性中,将mutations、actions的函数映射到组件的函数中,方便我们通过this.变量名
和this.函数名
来调用
- mapState:映射state的辅助函数,将vuex中state的数据,映射到 computed 计算属性中
- mapMutations:映射mutation的辅助函数,映射到 methods 中
- mapActions:映射action的辅助函数,映射到 methods 中
- mapGetters:映射getters的辅助函数,映射到 computed 计算属性中
核心概念 - state
state,状态,也就是数据,需要多个组件之间共享的数据
简单使用
- 在state中,定义状态变量
const store = new Vuex.Store({
// 状态,存放数据
state: {
uname: '万大爷',
count: 100,
},
});
- 在页面中,通过插值表达式使用状态变量
<template>
<div id="app">
<h1>根组件</h1>
<div>
<!-- 直接使用store实例,写起来太繁琐 -->
uname:{{ $store.state.uname }} count:{{ $store.state.count }}
</div>
</div>
</template>
优化,使用辅助映射函数
- vuex提供了辅助映射函数,来帮助我们将state状态数据,映射到vue组件的computed计算属性中,然后我们就可以直接使用,就不需要繁琐的使用
$store.state.属性名
来访问状态变量了
<template>
<div id="app">
<h1>根组件</h1>
<div>
<!-- 通过计算属性,获取状态数据 -->
uname:{{ uname }} count:{{ count }}
</div>
</div>
</template>
<script>
// mapState:映射state的辅助函数,将vuex中state的数据,映射到 computed 计算属性中
import { mapState } from "vuex";
export default {
computed: {
...mapState(["uname", "count"]),
}
}
</script>
核心概念 - mutations
- mutations,用来定义同步修改state状态的函数,在
mutations
中定义的函数,vuex调用时会传入2个参数 - 参数一:state状态,就是vuex的state状态对象
- 参数二:playload载荷,也就是参数,提交 mutation 时,传的参数(只能是1个参数,多个参数时,使用一个对象或数组包裹)
注意:不能直接修改state中的值,例如$store.state.count++
,默认情况下,vuex是允许这样修改数据的,但vuex不推荐这样做,应该遵循单向数据流,数据在vuex中,修改数据应该通过vuex的mutations或actions去修改,而不应该在子组件中直接修改,否则会导致排错困难
如果想vuex阻止在组件中,直接修改state,可以在vuex的Vuex.Store
构造函数,传入的配置对象中,配置strict
属性,设置为true,就可以在直接修改时,报错
简单使用
- 定义mutation函数
const store = new Vuex.Store({
// 同步修改state的函数,要求修改state,必须通过 mutation 函数
// 参数一:state状态,就是vuex的state状态对象
// 参数二:playload载荷,也就是参数,提交 mutation 时,传的参数(只能是1个参数,多个参数时,使用一个对象或数组包裹)
mutations: {
// 添加数量
addCount(state, playload) {
state.count += playload.num
},
// 更新数量,为指定数量
updateCount(state, count) {
state.count = count
}
},
});
- 在按钮点击事件中,调用mutation函数
<template>
<div id="app">
<h1>根组件</h1>
<div>
<!-- 违反vuex的单向数据流的原则,不要这样做 -->
<button @click="$store.state.count++">(错误)直接修改,count数据</button>
<br />
<!-- 修改state中的数据,要通过调用mutations中的函数来实现 -->
<button @click="fn1">(正确)通过mutations,修改count数据</button>
</div>
</div>
</template>
<script>
export default {
methods: {
// 触发 mutations 中的函数来修改状态
fn1() {
// commit(),提交函数的参数,是 mutations 中要执行的函数名
this.$store.commit("addCount")
// 传递参数,注意只能传一个参数,多个参数要用对象或数组包裹
this.$store.commit("addCount", {
num: 100,
num2: 200,
});
},
}
}
</script>
优化,使用辅助映射函数
- 和
mapState
类似,对于mutation,vuex也提供了mapMutations
函数,用来映射mutations中的函数到methods
方法中
<template>
<div id="app">
<h1>根组件</h1>
<div>
<!-- 违反vuex的单向数据流的原则,不要这样做 -->
<button @click="$store.state.count++">(错误)直接修改,count数据</button>
<br />
<!-- 修改state中的数据,要通过调用mutations中的函数来实现 -->
<button @click="fn1">(正确)通过mutations,修改count数据</button>
</div>
</div>
</template>
<script>
// mapMutations:映射mutation的辅助函数,映射到 methods 中
import { mapMutations } from "vuex";
export default {
methods: {
// 触发 mutations 中的函数来修改状态
fn1() {
// commit(),提交函数的参数,是 mutations 中要执行的函数名
// this.$store.commit("addCount")
// 传递参数,注意只能传一个参数,多个参数要用对象或数组包裹
// this.$store.commit("addCount", {
// num: 100,
// num2: 200,
// });
// 调用 mapMutations 映射后的函数
this.addCount({
num: 100,
num2: 200,
});
},
// 映射mutations中的函数,到methods中,然后就可以通过 this.addCount() 来调用 mutations 中的函数
...mapMutations(["addCount"]),
}
}
</script>
核心概念 - actions
- actions,定义异步修改state的函数,底层也是通过提交mutation来实现修改state
- actions中的函数,vuex调用时,都会传2个参数
- 参数一:context上下文,能获取到store身上的哪些属性和方法
- 参数二,参数,派发 action 时,传的参数,传的参数(只能是1个参数,多个参数时,使用一个对象或数组包裹)
简单使用
- 定义action函数
const store = new Vuex.Store({
actions: {
// 异步修改数据,异步修改数据必须通过调用 mutation 函数,也就是在 mutation 上套了一层action
// 参数一:context上下文,能获取到store身上的哪些属性和方法
// 参数二,参数,派发 action 时,传的参数,传的参数(只能是1个参数,多个参数时,使用一个对象或数组包裹)
updateAsyncCount(context, num) {
setTimeout(() => {
// 修改数据,提交mutation,同步修改数据
context.commit("updateCount", num)
console.log(`异步修改数据成功,新值为${num}`);
}, 1000);
}
},
});
- 在按钮点击事件中,派发action函数
<template>
<div id="app">
<h1>根组件</h1>
<div>
<button @click="fn2">异步,修改数据</button>
</div>
</div>
</template>
<script>
export default {
methods: {
// 触发 mutations 中的函数来修改状态
fn2() {
// 异步修改数据,触发action函数
// dispatch(),派发函数的参数,是 actions 中要执行的函数名
// 传递参数,同样也是智能传递一个参数,多个参数要用对象或数组包裹
this.$store.dispatch("updateAsyncCount", 888)
},
}
}
</script>
优化,使用辅助映射函数
<template>
<div id="app">
<h1>根组件</h1>
<div>
<button @click="fn2">异步,修改数据</button>
</div>
</div>
</template>
<script>
// mapActions:映射action的辅助函数,映射到 methods 中
import { mapActions } from "vuex";
export default {
methods: {
fn2() {
// 调用 mapActions 映射后的函数
this.updateAsyncCount(888);
},
// 映射actions中的函数,到methods中,然后就可以通过this.updateAsyncCount() 来调用 actions 中的函数
...mapActions(["updateAsyncCount"]),
}
}
</script>
核心概念 - getters
- 除了state之外,有时我们还需要从state中派生出一些状态,这些状态是依赖state的,此时会用到getters
- getters类似vue组件中的计算属性
基本使用
- 在state中,定义状态变量和getters
- getters中,定义total函数,计算状态变量list的值,得出一个总数,作为该函数的返回值
- 使用方式:
$store.getters.total
,每次都要写$store.getters
前缀,太繁琐
const store = new Vuex.Store({
// 状态,存放数据
state: {
list: [1, 2, 3, 4, 5, 6]
},
getters: {
// 类似计算属性,通过计算某个state,或处理多个state属性来得到一个结果
total(state) {
return state.list.reduce((prev, current) => {
return prev + current
}, 0)
}
},
});
- 在页面中,使用getters
<template>
<div id="app">
<h1>根组件</h1>
<div>
<!-- 使用vuex的getters属性 -->
gettes属性:{{ $store.getters.total }}
</div>
</div>
</template>
<script>
export default {
name: "App",
created() {
// 获取 getters 属性,类似于计算属性
console.log(this.$store.getters.total);
}
</script>
优化,使用辅助映射函数
<template>
<div id="app">
<h1>根组件</h1>
<div>
<!-- 使用vuex的getters属性 -->
gettes属性:{{ total }}
</div>
</div>
</template>
<script>
// mapGetters:映射getters的辅助函数,映射到 computed 计算属性中
import { mapGetters } from "vuex";
export default {
computed: {
// 映射vuex的getters属性
...mapGetters(["total"]),
}
}
</script>
模块化
- 由于 vuex 使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。(当项目变得越来越大的时候,Vuex会变得越来越难以维护)
创建和配置模块
- 单独定义一个模块
user.js
,同样配置state、mutations、actions、getters - 并通过
export default
暴露出该模块,准备给vuex注册模块 - 注意:
namespaced
属性,必须设置,否则调用时,会报错,说找不到该模块
// 用户模块的store
const state = () => ({
})
const getters = {
}
// 同步操作state数据
const mutations = {
}
// 异步操作state
const actions = {
}
export default {
// 开启命名空间,这样 mapState、mapMutations、mapActions等辅助函数,才能使用该模块
namespaced: true,
state,
getters,
mutations,
actions
}
- 在vuex中,注册模块
// Vuex相关配置,注意:Vue2的Vuex版本是3版本
import Vue from 'vue';
import Vuex from 'vuex';
// 引入用户模块的store
import user from '@/store/modules/user'
// 注册Vuex插件
Vue.use(Vuex);
// 创建仓库,管理数据
const store = new Vuex.Store({
...省略其他配置
// 模块
modules: {
// 模块名:模块对象
// user: user
user
}
});
// 默认导出
export default store
使用方式的变化
准备数据
// 用户模块的store
const state = () => ({
userInfo: {
name: 'zs',
age: 18
},
num: 100
})
const getters = {
// 返回格式化年龄的字符串
age(state) {
return state.num + '岁'
},
}
// 同步操作state数据
const mutations = {
// 修改 num 数据
updateNum(state) {
state.num++
}
}
// 异步操作state
const actions = {
updateAsyncNum(context) {
// 1秒后,提交mutation,更新state
setTimeout(() => {
context.commit("updateNum")
}, 1000);
}
}
export default {
// 开启命名空间,这样 mapState、mapMutations、mapActions等辅助函数,才能使用该模块
namespaced: true,
state,
getters,
mutations,
actions
}
关于state
使用模块后,之前的使用方式都需要加上模块名
- 普通方式获取,例如
- 未使用模块:
$store.state.属性名
- 使用模块:
$store.state.模块名.属性名
- 未使用模块:
- 使用mapState辅助映射函数
- 未使用模块:
...mapState(["属性名"])
- 使用模块:
...mapState("模块名", ["属性名"])
- 未使用模块:
<template>
<div id="app">
<h1>根组件</h1>
<div>
<!-- 模块数据,直接获取,太繁琐 -->
用户模块-name:{{ $store.state.user.userInfo.name }} 用户模块-age:{{
$store.state.user.userInfo.age
}}
<br />
<!-- 模块数据,使用mapState辅助函数 -->
用户模块-name:{{ userInfo.name }} 用户模块-age:{{ userInfo.age }}
</div>
</div>
</template>
<script>
// mapState:映射state的辅助函数,将vuex中state的数据,映射到 computed 计算属性中
import { mapState } from "vuex";
export default {
created() {
console.log(this.$store.state.user);
console.log(this.$store.state.user.userInfo.name);
console.log(this.$store.state.user.userInfo.age);
},
computed: {
// 映射vuex的state属性
...mapState("user", ["userInfo", "num"]),
}
}
</script>
关于mutations
使用模块后,之前的使用方式都需要加上模块名
- 普通方式获取,例如
- 未使用模块:
this.$store.commit("函数名")
- 使用模块:
this.$store.commit("模块名/函数名")
- 未使用模块:
- 使用mapMutations辅助映射函数
- 未使用模块:
...mapMutations(["函数名"])
- 使用模块:
...mapMutations("模块名", ["函数名"])
- 未使用模块:
<template>
<div id="app">
<h1>根组件</h1>
<div>
user模块的mutation:<button @click="fn3">{{ num }}</button>
</div>
</div>
</template>
<script>
// mapMutations:映射mutation的辅助函数,映射到 methods 中
import { mapMutations } from "vuex";
export default {
methods: {
fn3() {
// 调用user模块的mutation函数,格式:模块名/函数名
this.$store.commit("user/updateNum");
// 使用mapMutations辅助函数,映射后mutation函数
this.updateNum();
},
// 映射user模块的mutation函数
...mapMutations("user", ["updateNum"]),
}
}
</script>
关于actions
使用模块后,之前的使用方式都需要加上模块名
- 普通方式获取,例如
- 未使用模块:
this.$store.dispatch("函数名")
- 使用模块:
this.$store.dispatch("模块名/函数名")
- 未使用模块:
- 使用mapMutations辅助映射函数
- 未使用模块:
...mapActions(["函数名"])
- 使用模块:
...mapActions("模块名", ["函数名"])
- 未使用模块:
<template>
<div id="app">
<h1>根组件</h1>
<div>
user模块的action:<button @click="fn4">{{ num }}</button>
</div>
</div>
</template>
<script>
// mapActions:映射action的辅助函数,映射到 methods 中
import { mapActions } from "vuex";
export default {
methods: {
fn4() {
// 调用user模块的action函数,格式:模块名/函数名
this.$store.dispatch("user/updateAsyncNum")
// 使用mapActions辅助函数,映射后的action函数
this.updateAsyncNum();
},
// 映射user模块的action模块
...mapActions("user", ["updateAsyncNum"]),
}
}
</script>
关于getters
使用模块后,之前的使用方式都需要加上模块名
- 普通方式获取,例如
- 未使用模块:
$store.getters.属性名
- 使用模块:
$store.getters.[模块名/属性名]
- 未使用模块:
- 使用mapState辅助映射函数
- 未使用模块:
...mapGetters(["属性名"])
- 使用模块:
...mapGetters("模块名", ["属性名"])
- 未使用模块:
注意:getters和state虽然都是类似属性的变量,但是在加入模块后,写法是有区别的,例如
- state是:
$store.state.模块名.属性名
- getters是:
$store.getters['模块名/属性名']
- 错误写法:
$store.getters.模块名.属性名
可以看见,getters没有像state那样,在getters下再新建一个模块名的对象,而是在属性名的基础上增加模块和斜杠。那么在JS中,就必须使用中括号来访问了,也就是$store.getters['user/age']
,而不能是$store.getters.'user/age'
,因为这是不合法的
<template>
<div id="app">
<h1>根组件</h1>
<div>
<!-- 直接使用user模块的getter属性,太繁琐 -->
user模块的getters:<span>年龄 {{ $store.getters['user/age'] }}</span>
<br>
<!-- 使用mapGetters辅助函数,映射的getter属性 -->
user模块的getters:<span>年龄 {{ age }}</span>
</div>
</div>
</template>
<script>
// mapGetters:映射getters的辅助函数,映射到 computed 计算属性中
import { mapGetters } from "vuex";
export default {
created() {
// 获取user模块的getter属性
console.log(this.$store.getters['user/age']);
},
computed: {
// user模块中的getter
...mapGetters('user', ['age'])
}
}
</script>
网友评论