Vuex是什么?
- 官方回答:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到 Vue 的官方调试工具 devtools extension (opens new window),提供了诸如零配置的 time-travel 调试、状态快照导入导出等高级调试功能。
- 个人理解: 支持响应式的组件之间数据共享,通俗理解将数据作为全局变量,统一放在某个地方进行统一管理。
- 优点:
- 能够在vuex中集中管理共享的数据,便于开发和后期进行维护
- 能够高效的实现组件之间的数据共享,提高开发效率
- 存储在vuex中的数据是响应式的,当数据发生改变时,页面中的数据也会同步更新
- 缺点:数据的读取和修改vuex的数据需要经过,Actions或者Mutations来修改vuex的值,如果项目比较简单使用vuex就显得有些繁琐。
- 如下图所示:
ok 继续,其实通过vuex的使用就能窥探一二,
先写一下vuex的大概用法:
- 安装
Vuex
- 创建
Store
对象管理数据 - 挂载
Store
对象到vue
实例
实际操作
- 安装
Vuex
npm i -S vuex
- 创建
Store
对象管理数据
①创建src/store
目录,并在其中创建vuex.js
文件用于导入并创建store对象
②编写src/store/vuex.js
文件,基本代码如下:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
// state中存放的就是全局共享的数据
state: {count: 0}
})
- 挂载
Store
对象到vue
实例
import store from '@/store/vuex'
new Vue({
router,
store,
render: (h) => h(App),
}).$mount("#app");
上面的不重要:管中窥豹,如果要使用vuex,必须要挂载到Vue的实例上,并且在使用的时候用法为this.$store,挂载之后我们可以打印this来看看多了什么呢?
可以看到我们常用的$store,打开看看
是不是非常熟悉,常用的属性和方法都在,state、mutations、actions、getters等,只有一个属性state我们用来存储数据,其他的方法均用来进行操作数据使用。
那么根据官方文档提供的图:
- Devtools是vuex的调试工具,方便使用者进行调试。所以设置了Mutations来进行统一管理操作,这样所有的操作都会通过Mutations而不会有第二条路径,所以在Devtools监听的时候只需要盯着Mutations即可,但是如果出现了异步操作,那么就会出现问题了呀,所以出现了actions,当有异步操作时,通过Actions之后,异步完成在执行Mutationss的同步操作,最终改变state内的数据,从而达到改变视图的目的。
- 至于遗漏的getter以及模块化的工作,getter是对store中已有的数据加工处理形成新的数据对已有的数据进行加工除了,类似于Vue的计算属性,store数据发生变化,则getter中的数据也会跟着变化。
- 模块化这里不多阐述,在最后进行总结模块化。
继续深化:先来看看怎么使用Mutations和Actions
- 在Vuex中只能通过mutation变更store中的数据,不可以直接操作store中的数据
- 通过这种方式操作起来稍微繁琐一些,但是可以集中监控所有数据的变化
// 定义mutations
const sotre = new Vuex.Store({
state: {
count: 0
},
mutations: {
add(state[,arg]){
// 变更状态
state.count++
}
}
})
// 组件中触发mutation的第一种方式
methods:{
handle(){
this.$store.commit('add'[,arg])
}
}
// 组件中触发mutation的第二种方式
import {mapMutations} from 'vuex'
methods:{
...mapMutations(['add','reduce']),
handle1(){
this.add()
},
handle2(){
this.reduce([arg])
}
}
使用 Actions
// 声明action
const store = new Vuex.Store({
// 省略其他代码
mutations: {
add(state){
state.count++
}
},
actions: {
addAsync(context[,arg]){
setTimeout(() => {
context.commit('add'[,arg])
},1000)
}
}
})
// 组件中触发action
methods: {
handle(){
this.$store.dispach('addAsync'[,arg])
}
}
getters类似
// 定义getter
....
getters: {
showNum: state => {
return '当前最新的数量是【' + state.count + '】'
}
}
// 在组件中访问getters数据的第一种方式
this.$store.getters.全局数据名称
// 在组件中访问getters数据的第二种方式
// 按需导入mapGetters函数
import {mapGetters} from 'vuex'
// 将全局函数映射为当前组件的计算属性
computed: {
...mapGetters(['showNum'])
}
- 怎么使用不是重点,重点是我们要知道,为什么要使用computed而不是data或者methods获取vuex中的state、getters呢?因为data 中的内容只会在 created 钩子触发前初始化一次,赋值之后属性的值就是纯粹的字面量,之后this.$store.state.xxx 如何变化均影响不到count的取值。
对比bus总线
看了这么多东西,你是不是也觉得和bus总线非常的相似,但是他们有本质的区别,并且通过vuex响应式的特点可以清楚的知道,他和数据双向绑定的原理一样,使用了getter和setter进行数据劫持。
- 本质上最大的区别:bus利用事件抛发的原理进行传递数据而vuex通过数据劫持,并且复制一份相同的_data来进行数据管理.
- 举个栗子:bus相当于打电话,中间商只是接到电话消息传递给第三方,而vuex是你写的消息,它保留一份下来,必须要通过它来管理你的消息,不管是你要怎么处理消息。还有一个好处,vuex分离了数据和视图,让代码更加的清晰和方便维护。
- 个人理解:其实在跨组件传输中,本质上都是一个方法,因为我们所有的组件也好,操作也好都是在vue这个实例上去实现的,所以Vue就是所有组件都可以访问到的一个对象,那么我们将数据保存在Vue的prototype上时,无论我们在哪一个组件,都可以访问到数据了。
模块化思想
- 为什么有状态的模块化?主要是因为项目是多人协作开发的,如果都去修改一个文件,则经常会出现代码冲突,而解决冲突比较费事费力。
- 使用步骤
- 建立src/store/modules文件夹(名称随意)
- 在modules文件夹中建立需要的模块文件(命名以功能为导向,记得导出一下)
- 注意点1(了解):
- 在模块的时候,因为多人合作,不能的开发者之前并不清楚其他怎么给方法和数据源进行命名,这样的话就有一个问题:万一名称重名怎么办?如果冲突了,会执行以下合并策略:
- state数据源肯定不会冲突,它以模块进行保存
- mutations、actions的方法不会以模块为单位进行保存,如果出现同名则可能会冲突。vuex会先将这些同名的方法,整合到一起,都去执行。会先执行index.js中的,再去执行其他的。
- getters如果出现冲突,不给解决,直接报错。
- 因为多人合作可能出现命名的冲突,特别针对getters,vuex模块化的时候支持使用
命名空间
- 默认是没有给模块开启命名空间的
- 如果需要请自己开启,通过模块对象的属性“namespaced”,将其值设置为true
- 命名空间的名称,是模块的名字(模块里面属性的名字)
- 在模块的时候,因为多人合作,不能的开发者之前并不清楚其他怎么给方法和数据源进行命名,这样的话就有一个问题:万一名称重名怎么办?如果冲突了,会执行以下合并策略:
- 注意点2:由于模块使用了命名空间,所以之前没有模块化的使用方式(this、map系列)在模块化之后都要发生对应的变化
- state
- this形式:this.$store.state.空间名.xxxx
- map系列:…mapSate(空间名,[xxxx,yyyy,zzzz…])
- mutations
- this形式:this.$store.commit(“空间名/方法名”, “参数”);
- map系列:…mapMutations(“空间名”,[“方法名”,…]),
- actions
- this形式:this.$store.dispatch(“空间名/方法名”, “参数”);
- map系列:…mapActions(“空间名”,[“方法名”,…]),
- getters
- this形式:this.$store.getters[“空间名/属性名”]
- map形式:…mapActions(“空间名”, [“属性名”,…]),
- state