前置知识:
什么是Vuex
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式 + 库。它采用集中式存储管理应用的所有组件的状态,
并以相应的规则保证状态以一种可预测的方式发生变化
Vue 它实际是解决非关系数据之间传输或者不同组件数据共享的问题,通过几个部分进行数据之间的交互:
- 状态,驱动应用的数据源;
- 视图,以声明方式将状态映射到视图;
- 操作,响应在视图上的用户输入导致的状态变化。
适用范围
Vuex 可以帮助我们管理共享状态,并附带了更多的概念和框架。这需要对短期和长期效益进行权衡。
如果您打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。如果应用够简单,最好不要使用 Vuex。
一个简单的 store 模式就足够所需了。但是,如果需要构建一个中大型单页应用,可能会考虑如何更好地在组件外部管理状态,
Vuex 将会成为自然而然的选择。
前期准备:需要提前安装 vue/cil 脚手架
1. 使用包管理工具 下载
vue create demo
cd demo
# yarn 下载
yarn add vuex@3.5.1 --save
# npm 下载
npm install vuex@3.5.1 --save
2. 在main.js 中配置 Vuex
import Vue from 'vue'
import App from './App.vue'
// 导入 Vuex
import Vuex from 'vuex';
Vue.config.productionTip = false
// 在Vue中使用 Vuex (相当于调用 vuex 的 install 函数 )
Vue.use(Vuex);
// 实例化 vex 并进行配置
const store = new Vuex.Store({});
new Vue({
render: h => h(App),
// Vuex 注入到 vue实例
store
}).$mount('#app')
通过掌握基础使用来逐渐了解 Vuex 使用原理
简介
state是放置所有公共状态的属性,如果有一个公共状态数据,只需要定义在 state 对象中
定义
通过 main.js 的 Vuex.Store({}) 内定义
const store = new Vuex.Store({
state:{
// 管理数据
count:10,
},
});
使用
state的使用分为 原始形式 和 计算属性 还有 辅助函数
原始使用 – 插值表达式
组件可以使用 this.$store 获取到 vuex 中的 store 对象实例,可通过 state 属性获取 count
<template>
<div>store的数据:{{ $store.state.count }}</div>
</template>
...
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
state:{
count:10,
},
});
...
效果展示
计算属性
将 state 属性定义在计算属性中
<template>
<div>store的数据:{{ count }}</div>
</template>
export default {
computed: {
count() {
return this.$store.state.count;
}
}
}
...
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
state:{
count:10,
},
});
...
效果展示
辅助函数 – mapState
mapState 是辅助函数,通过该函数可帮助我们把 store 中的数据映射到组件的 计算属性 中
<template>
<div>store的数据:{{ count }}</div>
</template>
<script>
import { mapState } from 'vuex';
export default {
computed: {
// 采用数组形式引入 state 属性
...mapState(['count'])
}
}
</script>
...
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
state:{
count:10,
},
});
...
效果展示
简介
state 数据修改只能通过 mutations,并且 mutation 必须是同步更新,目的是形成快照
数据快照:一次 mutation 的执行,立刻得到一种视图状态,因为是立刻,所以必须是同步
注意: Vuex中 mutaions 中要求不能写异步代码,如果有异步的 ajax请求,应该放在 actions 中
定义
通过 main.js 的 Vuex.Store({}) 内定义
const store = new Vuex.Store({
state: {
count: 10,
},
// 修改 state 必须通过 mutations
mutations: {
}
}
});
格式说明
mutations 是一个对象,对象中存放着修改 state 的方法。它接收两个参数:
- 参数1:store 对象,通过该参数可以直接获取 store 中的 state 存储的值
- 参数2:payload载荷,允许在使用 mutations 的方法中向函数中传递参数
const store = new Vuex.Store({
state: {
count: 10,
},
mutations: {
// 参数一 存放当前 store 的 state 属性
// 参数二 是 payload载荷,调用 mutaiions 用于传递参数
addCount(state, num) {
state.count += num;
}
}
});
new Vue({
render: h => h(App),
store
}).$mount('#app')
使用
mutations 的使用分为 原始形式 和 辅助函数,并可以对 mutations 中的方法进行自定义传参
原始形式 – this.$store.commit(方法名)
通过 commit 写入对应的字符串函数名调用对应 mutations 方法
<template>
<div id="app">
<div>store的数据:{{ count }}</div>
<button @click="addCount">+1</button>
</div>
</template>
<script>
import { mapState } from 'vuex';
export default {
computed: {
...mapState(['count'])
},
methods:{
addCount(){
this.$store.commit('addCount');
},
}
}
</script>
效果展示
辅助函数 – mapMutations()
mapMutations 是辅助函数,通过该函数可帮助我们把 mutations 中的方法映射到组件的 methods 中
<template>
<div id="app">
<div>store的数据:{{ count }}</div>
<button @click="addCount">+1</button>
</div>
</template>
<script>
import { mapState } from 'vuex';
import { mapMutations } from 'vuex';
export default {
computed: {
...mapState(['count'])
},
methods: {
...mapMutations(['addCount'])
}
}
</script>
效果展示
函数传参
通过 mutations 中的方法的参数2定义和接收,对函数进行自由传参;
注意:如若 mutations 方法定义了形参,但是调用方不对该函数传参,vue会默认在形参中传入事件对象
const store = new Vuex.Store({
state: {
count: 10,
},
mutations: {
addCount(state, num = 1) {
state.count += num;
}
}
});
<template>
<div id="app">
<div>store的数据:{{ count }}</div>
<button @click="addNum">原始形式方式 +5</button>
<button @click="addCount(3)">辅助函数方式 +3</button>
</div>
</template>
<script>
import { mapState } from 'vuex';
import { mapMutations } from 'vuex';
export default {
computed: {
...mapState(['count'])
},
methods: {
addNum() {
this.$store.commit('addCount', 5);
},
...mapMutations(['addCount']),
}
}
</script>
效果展示
简介
actions 是负责 vuex 中主要负责进行异步操作
state 是存放数据的,mutations 是同步更新数据,actions 则负责进行异步操作
定义
通过 main.js 的 Vuex.Store({}) 内定义
const store = new Vuex.Store({
state: {
...
},
mutations: {
...
},
actions: {
getAsyncCount() {
...
}
}
});
格式说明
在 actions 中定义的异步数据,取得的数据一般为 ajax请求得到的结果,不会立即返回,
当返回数据后,actions 中的方法通过调用 mutations 方法即可实现修改 state 中的数据。
actions 中定义的方法接收两个参数:
- 参数1:context 表示当前 store 的实例,可以通过 context.state 获取状态,
也可以通过 context.commit 来提交 mutations。也可以通过 context.diapatch 调用其他的 action - 参数2:payload载荷,允许在使用 actions 的方法中向函数中传递参数
const store = new Vuex.Store({
state: {
count: 0,
},
mutations: {
addCount(state) {
state.count++;
}
},
actions: {
getAsyncCount(context) {
setTimeout(() => {
// 3秒后 执行 addCount 方法
context.commit('addCount');
}, 3000);
}
}
});
使用
actions 使用方式分为 原始调用 和 辅助函数,并可以对 actions 中的方法进行自定义传参
原始调用 – this.$store.dispatch(方法名);
actions 会等待异步的结果返回,得到结果后可以调用 mutations 中的方法立即执行
<template>
<div>
<div>store的数据:{{ $store.state.count }}</div>
<button @click="asyncAddNum">等待3秒后 +1</button>
</div>
</template>
<script>
export default {
methods: {
asyncAddNum() {
this.$store.dispatch('asyncAddCount');
},
},
}
</script>
const store = new Vuex.Store({
state: {
count: 0,
},
mutations: {
addCount(state) {
state.count++;
},
},
actions: {
asyncAddCount(context) {
// 模拟异步执行
setTimeout(() => {
// 调用 mutations 中的方法
context.commit('addCount');
}, 3000);
},
},
});
效果展示
辅助函数 – mapActions()
mapActions 是辅助函数,通过该函数可帮助我们把 actions 中的方法映射到组件的 methods 中
<template>
<div id="app">
<div>store的数据:{{ $store.state.count }}</div>
<button @click="asyncAddCount">等待3秒后 +1</button>
</div>
</template>
<script>
import { mapActions } from 'vuex';
export default {
methods: {
...mapActions(['asyncAddCount']),
},
}
</script>
const store = new Vuex.Store({
state: {
count: 0,
},
mutations: {
addCount(state) {
state.count++;
},
},
actions: {
asyncAddCount(context) {
// 模拟异步执行
setTimeout(() => {
// 调用 mutations 中的方法
context.commit('addCount');
}, 3000);
},
},
});
效果展示
函数传参
通过 actions 中的方法的参数2定义和接收,对函数进行自由传参;
注意:如若 actions 中的方法定义了形参,但是调用方不对该函数传参,vue会默认在形参中传入事件对象
<template>
<div id="app">
<div>store的数据:{{ $store.state.count }}</div>
<button @click="asyncAdd">原始调用 等待3秒后 +233</button>
<button @click="asyncAddCount(233)">辅助函数 等待3秒后 +233</button>
</div>
</template>
<script>
import { mapActions } from 'vuex';
export default {
methods: {
...mapActions(['asyncAddCount']),
asyncAdd(){
this.$store.dispatch('asyncAddCount',233);
}
},
}
</script>
const store = new Vuex.Store({
state: {
count: 0,
},
mutations: {
addCountNum(state, num) {
state.count += num;
},
},
actions: {
// context 表示当前 store 的实例,可以通过 context.commit 来提交 mutations
// 参数2:允许在使用 actions 的方法中向函数中传递参数
asyncAddCount(context, num) {
setTimeout(() => {
context.commit('addCountNum', num);
}, 3000);
},
},
});
效果展示
简介
getters 是 veux 实例中,充当计算属性的对象。除 state 之外,有时候我们还需要从 state 中派生出一些状态,
这些状态是依赖 state 的。此时会用到 getters
定义
通过 main.js 中 store 实例中的 getters 对象去定义;
const store = new Vuex.Store({
state: {
age: 19,
},
// getter 函数的第一个参数是 state
// 必须要有返回值
getters: {
isAdult: state => state.age > 18 ? '成年' : '未成年',
}
});
使用
vue组件通过 原始方式 和 辅助函数 来获取 getters 中的数据
原始方式 – $store.getters
<template>
<div id="app">
<div>{{ $store.getters.filterList }}</div>
</div>
</template>
const store = new Vuex.Store({
state: {
list: [1, 2, 3, 4, 5, 6, 7, 8, 9],
},
// getter 函数的第一个参数是 state
// 必须要有返回值
getters: {
filterList: state => state.list.filter(item => item > 5)
}
});
效果展示
辅助函数 – mapGetters(方法名)
<template>
<div id="app">
<div>{{ filterList }}</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
export default {
computed: {
...mapGetters(['filterList'])
}
}
</script>
const store = new Vuex.Store({
state: {
list: [1, 2, 3, 4, 5, 6, 7, 8, 9],
},
// getter 函数的第一个参数是 state
// 必须要有返回值
getters: {
filterList: state => state.list.filter(item => item > 5)
}
});
效果展示
通过以下表格展示关于 state、mutations、actions 之间的功能和调用方式
对象 | 描述 | 原始方式 | 辅助函数 |
---|---|---|---|
state | 存放vex的所有公共状态数据 | $store.state.值 | mapState() |
mutations | mutations 通过定义同步方法可去操作或修改 state 数据 | this.$store.commit() | mapMutations() |
actions | actions 是负责 vuex 中主要负责进行异步操作 | this.$store.dispatch() | mapActions() |
getters | getters 是 veux 实例中,充当计算属性的对象 | this.$store.getters() | mapGetters() |
模块化的概念设定是为了解决并提高项目开发的维护性
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。这使得 store 在后期开发中变得相当臃肿
用途
通过使用 modules 对象,使各个数据拆分,能针对性的进行分类的维护
定义
将项目中所有大的逻辑和业务根据功能拆分成各个小块,通过 modules 中定义各个小块的子模块;实现业务的拆分
- 定义两个模块 user 和 setting
- user 中管理用户状态 token
- setting 中管理应用的名称 name
const store = new Vuex.Store({
modules: {
user: {
state: {
token: 'smmcatmaxpro'
},
mutations: {},
actions: {}
},
setting: {
state: {
name: '屎猫猫'
},
mutations: {},
actions: {}
}
}
});
应用
方式一:通过 $store.state.模块名称.属性名 方式调用 modules 中创建的子模块的 state 成员
<template>
<div id="app">
<ul>
<li><b>token值:</b>
{{ $store.state.user.token }}
</li>
<li><b>姓名:</b>
{{ $store.state.setting.name }}
</li>
</ul>
</div>
</template>
效果展示
方式二:通过 根级别的 getters 对象中设置 成员 并引用不同模块的 数据,达到快捷访问的效果
const store = new Vuex.Store({
modules: {
...
},
getters: {
token: state => state.user.token,
name: state => state.setting.name,
}
});
<template>
<div id="app">
<ul>
<li><b>token值:</b>
{{ token }}
</li>
<li><b>姓名:</b>
{{ name }}
</li>
</ul>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
export default {
computed: {
...mapGetters(['token', 'name']),
}
}
</script>
效果展示
描述
默认情况下,模块内部的 action、mutation 和 getter 是注册在全局命名空间的。
这使得多个模块能对同一 mutation 或 action 做出响应
因此,各个子模块都可以直接通过全局的方式调用。并无封闭性;
调用子模块方法
子模块定义的 mutation 方法允许通过 commit 全局直接调用执行
【不封闭性】模块化的 mutation 方法无论是使用 原始调用 和 辅助函数 方式都可以直接通过方法名直接调用,
这导致了模块之间毫无隐私,且缺乏高封闭性
const store = new Vuex.Store({
modules: {
user: {
state: {
token: 'smmcatmaxpro'
},
mutations: {
updateToken(state) {
state.token = 'maxsmmnacercat'
}
},
actions: {}
},
setting: {
...
},
getters: {
token: state => state.user.token,
name: state => state.setting.name,
}
});
<template>
<div id="app">
<ul>
<li><b>token值:</b>
{{ token }}
</li>
<li><b>姓名:</b>
{{ name }}
</li>
</ul>
<button @click="updateToken">改变token值</button>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
export default {
computed: {
...mapGetters(['token', 'name']),
},
methods:{
updateToken(){
this.$store.commit('updateToken');
}
}
}
</script>
效果展示
命名空间加锁
使用 namespaced 属性设置为 true ;将保证内部模块的高封闭性。而不会全局直接调用
【高封闭性】模块的本质是希望互不干扰,各顾各的。通过命名空间 namespaced 像是给模块加锁的操作。
被设置 namespaced:true 的模块的内部数据将不允许被直接调用
设锁的模块中的功能被调用时,会抛出 “[vuex] unknown mutation type: updateToken” 错误。
modules: {
user: {
namespaced: true,
state: {
token: 'smmcatmaxpro'
},
mutations: {
updateToken(state) {
state.token = 'maxsmmnacercat'
}
},
actions: {}
}
});
访问带命名空间模块
方案1:直接调用 – 带上模块的属性名路径 例如:user/updateToken
<template>
<div id="app">
<ul>
<div>{{ $store.state.user.token }}</div>
</ul>
<button @click="updateToken">改变token值</button>
</div>
</template>
<script>
export default {
methods:{
updateToken(){
this.$store.commit('user/updateToken');
}
}
}
</script>
效果展示
方案2:辅助函数 – 带上模块的属性名路径 例如:user/updateToken,并嵌套在另一个方法中执行
【注意】从 mapMutations 调出的 user/updateToken 是以原本的名字为函数名。
这个名字作为函数名是不合法的。因此直接执行会报错!
通过导入后,嵌套在另一个方法中,通过 Vue实例挂载的 this 指向该函数并执行的方式可以实现 该函数的执行
<template>
<div id="app">
<ul>
<div>{{ $store.state.user.token }}</div>
</ul>
<button @click="test">改变token值</button>
</div>
</template>
<script>
import { mapMutations } from 'vuex';
export default {
methods:{
...mapMutations(['user/updateToken']),
test(){
this['user/updateToken']();
}
}
}
</script>
效果展示
方案3:使用 createNamespacedHelpers 创建基于某个命名空间辅助函数
【注意】与 vuex 引入的 mapMutations 存在命名冲突,注意需要手动更改名称
例如:const userMapmutations = createNamespacedHelpers(‘user’).mapMutations;
<template>
<div id="app">
<ul>
<div>{{ $store.state.user.token }}</div>
</ul>
<button @click="updateToken">改变token值</button>
</div>
</template>
<script>
import { createNamespacedHelpers } from 'vuex';
const { mapMutations } = createNamespacedHelpers('user');
export default {
methods: {
...mapMutations(['updateToken']),
}
}
</script>
效果展示