Vuex是什么?
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到 Vue 的官方调试工具 devtools extension,提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。
什么情况下应该使用 Vuex?
虽然 Vuex 可以帮助我们管理共享状态,但也附带了更多的概念和框架。这需要对短期和长期效益进行权衡。
如果您不打算开发大型单页应用,使用 Vuex 可能是繁琐冗余的。确实是如此——如果您的应用够简单,您最好不要使用 Vuex。一个简单的 store 模式就足够您所需了。但是,如果您需要构建一个中大型单页应用,您很可能会考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择。引用 Redux 的作者 Dan Abramov 的话说就是:
Flux 架构就像眼镜:您自会知道什么时候需要它。
vuex解决了什么问题?
- 多个组件依赖于同一状态时,对于多层嵌套的组件的传参将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。
- 来自不同组件的行为需要变更同一状态。以往采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致无法维护的代码。
vuex基本配置和使用?
第一步:安装
首先,vuex 分 3.x 版本和4.x版本,分别对应vue2.0与3.0也就是说,vue2.0只能安装vuex3.x版本,最高3.6.2,vue3.0才能装vuex4.x版本我使用的是 npm,且为 vue2.0版本,该环境安装流程应为: npm install vuex --save
安装成功后可以在 package.json 文件夹中找到 vuex 的版本;
"dependencies": {
"element-ui": "^2.15.5",
"vue": "^2.5.2",
"vue-router": "^3.0.1",
"vuex": "^3.6.2"
},
第二步:注册并使用
我们在项目中的 src 目录下,创建 store 目录,用在 store 目录中创建 index.js 文件,index.js 内添加以下代码:
import Vue from 'vue'
// 引入vuex
import Vuex from 'Vuex'
Vue.use(Vuex)
// 导出一个vuex实例
export default new Vuex.Store({
// 配置内容
state: {
},
mutations: {
}
})
然后我们需要在 main.js 导入
import store from './store'
同时 vue 实例中添加 store 属性,即可在全局的所有子组件中使用。
new Vue({
el: '#app',
router,
store,
components: { App },
template: '<App/>'
})
vuex常见属性介绍
vuex中属性有五种,分别是 State、 Getter、Mutation 、Action、 Module。
State特性
-
Vuex就是一个仓库,仓库里面放了很多对象。其中state就是数据源存放地,对应于一般Vue对象里面的data。 -
state里面存放的数据是响应式的,Vue组件从store中读取数据,若是store中的数据发生改变,依赖这个数据的组件也会发生更新 -
它通过
mapState、mapGetters把全局的state和getters映射到当前组件的computed计算属性中 -
获取状态的方式
this.$store.state.xx
state: {
count: 1
},
Getter特性
-
getters可以对State进行计算操作,它就是Store的计算属性 -
虽然在组件内也可以做计算属性,但是
getters可以在多组件之间复用 -
如果一个状态只在一个组件内使用,是可以不用
getters
Mutation特性
Mutation是变更state的唯一方式,mutation中只能同步提交,异步在这里提交devtools监测不到数据的变化。
mutations: {
increment (state) {
// 变更状态
state.count++
}
}
- 使用
commit方式提交mutation,同时可以向store.commit中传入其他的参数,即mutation的载荷。
// mutation中通过store.commit('mutation中的方法名',参数),
this.store.commit('increment', 10)
// 在mutations中接收。
mutations: {
increment (state, n) {
state.count += n
}
}
- 同时可以用对象风格的提交方式(提交
mutation的另一种方式是直接使用包含type属性的对象)
store.commit({
type: 'increment',
amount: 10
})
// mutation中接收
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
mutation中使用mapMuatation映射。
Actions特性
Action类似于mutation,不同在于Action提交的是mutation,而不是直接变更状态;
actions: {
increment (context) {
context.commit('increment')
}
}
action 中可以执行异步操作。
actions: {
incrementAsync ({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
}
actions 使用 store.dispatch() 方法触发 mutations ,
store.dispatch('increment')
Actions 支持同样的载荷方式和对象方式
// 以载荷形式分发
store.dispatch('incrementAsync', {
amount: 10
})
// 以对象形式分发
store.dispatch({
type: 'incrementAsync',
amount: 10
})
同时可以使用映射的方式注册组件的事件。
import { mapActions } from 'vuex' //引入mapActions映射函数
//在方法中有两种映射写法
//第一种
...mapActions({
组件中的方法名:actions中的方法名
})
//第二种
当action中的方法名和methods中的方法名相同时
...mapActions(['方法名'])
Action 可以包含任意异步操作。actions 中也可以变更状态,但是不建议这样中,因为 devtools 中不能检测数据的变化。
Module特征
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。 为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:
const moduleA = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... }
}
const store = createStore({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
图解:
vuex案例
如图所示:实现一个加减和等会+的功能,以及应用一下Getter
模版代码展示
<template>
<div>
<div class="box">
<h3 class="title">vuex的使用</h3>
<p>{{ $store.state.count }}</p>
<p>
<el-button @click="increment">+</el-button>
<el-button @click="decrement">-</el-button>
<el-button @click="incrementTime">等会+</el-button>
</p>
<!-- 学生组件 -->
<Student />
</div>
</div>
</template>
点击事件对应的方法
methods: {
// 触发mutation
increment() {
this.$store.commit({
type: "increment",
num: 20,
});
},
// 触发mutation
decrement() {
this.$store.commit("decrement");
},
// 触发actions
incrementTime() {
this.$store.dispatch("incre");
},
// 使用mapActions辅助函数将组件中的方法映射store.dispatch()
// 第一种写法:
// ...mapActions(['incre'])
// 第二种写法:
// ...mapActions({
// incre: 'incre'
// })
}
store中的代码
/* eslint-disable */
import Vue from 'vue'
// 引入vuex
import Vuex from 'Vuex'
Vue.use(Vuex)
// state---用于存储数据
const state = {
count: 0,
persons: [
{
id: 1,
name: '张三',
isStudent: false
},
{
id: 2,
name: '李四',
isStudent: false
},
{
id: 3,
name: 'tom',
isStudent: true
},
]
}
// mutation----- 用于操作数据
const mutations = {
// increment中的第一个参数是state,第二个参数是commit提交的值
increment(state, params) {
console.log(params);
state.count++
},
incrementSync(state, params) {
state.count += params
},
decrement: state => state.count--
}
// actions ----用于响应式动作
const actions = {
// 处理异步操作
incre({ commit }) {
setTimeout(() => {
commit('incrementSync', 10)
}, 1000)
}
}
//getters ----相当于计算属性,可以用于缓存数据
const getters = {
studentName(state) {
// Getter 接受 state 作为其第一个参数,也可以不传,这里相当于计算属性,可以对结果进行缓存,在其他地方可以通过this.$store.getters获取缓存的结果
// 好处是不需要在每个组件写计算属性,在getters中的计算结果可以在多个组件中复用
return state.persons.find(i => i.isStudent)
},
//同时getters中可以通过方法去访问
getPersonID({ persons }) {
return (val) => {
return persons.find(i => i.id === val).name
}
}
}
// 导出一个vuex实例
export default new Vuex.Store({
// 配置内容
state,
mutations,
actions,
getters
})
学生组件代码展示
<template>
<div class="box">
<h3 class="title">我是学生组件</h3>
<h4>数据: {{ count }}</h4>
<h4>查找学生:{{ personName }}</h4>
<h4>查找学生ID展示学生姓名:{{ personID }}</h4>
</div>
</template>
<script>
export default {
computed: {
count() {
return this.$store.state.count;
},
personName() {
// 通过属性的方式访问
return this.$store.getters.studentName.name;
},
personID() {
return this.$store.getters.getPersonID(1);
},
},
};
</script>
状态管理和路由传参的区别是什么?
状态管理是全局响应式数据,跨页面传递数据,刷新页面数据会丢失,可以使用本地缓存,路由传参非响应式,传参方式分为两种,query 传参,页面刷新数据不会丢失。params 传参分为两种,跳转时传入,刷新页面会丢失,配置路由时参入不会丢失。
Vuex中状态是对象时,使用时要注意什么?
因为对象是引用类型,复制后改变属性还是会影响原始数据,这样会改变 state 里面的状态,是不允许,所以先用深度克隆复制对象,再修改。
简述vuex的数据传递流程?
当组件进行数据修改的时候我们需要调用 dispatch 来触发 actions 里面的方法。actions 里面的每个方法中都会 有一个 commit 方法,当方法执行的时候会通过 commit 来触发 mutations 里面的方法进行数据的修改。 mutations 里面的每个函数都会有一个 state 参数,这样就可以在 mutations 里面进行 state 的数据修改 ,当数据修改完毕后,会传导给页面。页面的数据也会发生改变
单一状态树
Vuex 使用单一状态树——是的,用一个对象就包含了全部的应用层级状态。至此它便作为一个“唯一数据源 (SSOT)”而存在。这也意味着,每个应用将仅仅包含一个 store 实例。单一状态树让我们能够直接地定位任一特定的状态片段,在调试的过程中也能轻易地取得整个当前应用状态的快照。
单状态树和模块化并不冲突——在后面的章节里我们会讨论如何将状态和状态变更事件分布到各个子模块中。
vuex中为什么把异步操作封装在action,把同步操作放在mutations?
区分 actions 和 mutations 并不是为了解决状态问题,而是为了能用 devtools 追踪状态变化。
事实上在 vuex 里面 actions 只是一个架构性的概念,并不是必须的,说到底只是一个函数,你在里面想干嘛都可以,只要最后触发 mutation 就行。异步状态态怎么处理那是用户自己的事情。vuex 真正限制你的只有 mutation 必须是同步的这一点(在 redux 里面就好像 reducer 必须同步返回下一个状态一样)。
同步的意义在于这样每一个 mutation 执行完成后都可以对应到一个新的状态(和 reducer 一样),这样 devtools 就可以打个 snapshot 存下来,然后就可以随便 time-travel 了。
如果你开着 devtool 调用一个异步的 action,你可以清楚地看到它所调用的 mutation 是何时被记录下来的,并且可以立刻查看它们对应的状态。其实我有个点子一直没时间做,那就是把记录下来的 mutations 做成类似 rx-marble 那样的时间线图,对于理解应用的异步状态变化很有帮助。