vuex详解
在使用vue组件开发中,我们需要解决多个组件间的数据通信和状态管理就显得难以维护的问题。然后通过props和事件在爷/父/孙进行通信,在多层组件嵌套的情况下,代码过于臃肿,耦合过深,不利于维护和扩展。这个时候就需要Vuex了,把需要A中的state存储起来,大家需要的直接从Vuex获取.并且Vuex是响应式的,动态更新.。
总而言之,vuex是用于解决组件之间数据共享,数据的集中处理,属于单项数据流。
基本使用
#安装 npm install --save vuex #引入 在src根目录下创建一个store的文件夹, #下面写index.js来实现相关的存储方法(组件共用数据) import Vuex from 'vuex' import Vue from 'vue' Vue.use(Vuex)
vuex的组成部分(应用结构)
State
State负责存储整个应用的状态数据,一般需要在使用的时候在跟节点注入store对象,后期就可以使用this.$store.state直接获取状态。一般是在main.js文件中引入store的文件,从而使用。(vue spa应用中)
//store为实例化生成的
import store from './store'
new Vue({
el: '#app',
store,
render: h => h(App)
})
这个store可以理解为一个容器,包含着应用中的state等。实例化生成store的过程是:
const mutations = {...};
const actions = {...};
const state = {...};
Vuex.Store({
state,
actions,
mutation
});
后续在组件中使用的过程中,如果想要获取对应的状态你就可以直接使用this.$store.state获取,当然,也可以利用vuex提供的mapState辅助函数将state映射到计算属性中去,如
// 我是组件
let name=this.$store.state.name
import {mapState} from 'vuex'
export default {
computed: mapState({
count: state => state.count
})
}
Mutations
Mutations的中文意思是“变化”,利用它可以更改状态,本质就是用来处理数据的函数,其接收唯一参数值state。store.commit(mutationName)是用来触发一个mutation的方法。需要记住的是,定义的mutation必须是同步函数,否则devtool中的数据将可能出现问题,使状态改变变得难以跟踪。
const mutations = {
mutationName(state) {
//在这里改变state中的数据
}
}
在组件中触发:
//我是一个组件
export default {
methods: {
handleClick() {
this.$store.commit('mutationName')
}
}
}
或者使用辅助函数mapMutations直接将触发函数映射到methods上,这样就能在元素事件绑定上直接使用了。如:
import {mapMutations} from 'vuex'
//我是一个组件
export default {
methods: mapMutations([
'mutationName'
])
}
Actions
Actions也可以用于改变状态,不过是通过触发mutation实现的,重要的是可以包含异步操作。其辅助函数是mapActions与mapMutations类似,也是绑定在组件的methods上的。如果选择直接触发的话,使用this.$store.dispatch(actionName)方法。action内部支持异步方法,这是很重要的一点区别。
//定义Actions,以下方法用了es5的解构
const actions = {
actionName({ commit }) {
//dosomething
commit('mutationName')
}
}
//等同于下面的代码
const actions = {
actionName({context ) {
//dosomething
context.commit('mutationName')
}
}
在组件中使用
import {mapActions} from 'vuex'
//我是一个组件
export default {
methods: mapActions([
'actionName',
])
}
//分发action
store.dispatch('actionname')
Getters
有些状态需要做二次处理,就可以使用getters。通过this.$store.getters.valueName对派生出来的状态进行访问。或者直接使用辅助函数mapGetters将其映射到本地计算属性中去。这个属性相当于vuex的计算属性。
const getters = {
strLength: state => state.aString.length
}
//上面的代码根据aString状态派生出了一个strLength状态
在组件中使用
import {mapGetters} from 'vuex'
//我是一个组件 ,可以利用mapGetters
export default {
computed: mapGetters([
'strLength'
])
}
//直接使用getters
computed:{namelen(){
return this.$store.getters.namelen
}
}
Plugins
插件就是一个钩子函数,在初始化store的时候引入即可。比较常用的是内置的logger插件,用于作为调试使用。
import createLogger from 'vuex/dist/logger'
const store = Vuex.Store({
...
plugins: [createLogger()]
})
模块化
随着项目的复杂性增加,我们共享的状态越来越多,这时候我们就需要把我们状态的各种操作进行一个分组,分组后再进行按组编写。
// 一般我们用常量申明模块组:
const moduleA={
state,mutations,getters,actions
}
export default new Vuex.Store({
modules:{a:moduleA}
})
// 基本使用 ,加上state.a的命名空间即可
computed:{
count(){
return this.$store.state.a.count;
}
}
注意事项
- vuex与持久存储localStorage的关系
场景 :发现部分开发在引入持久存储之后,mutation的时候没有改变state,而是直接修改的localStorage,这样会导致没有页面刷新时,拿到的state都是不对的。因为state不会实时监听localStorage的更改。
解决方案 :
1. 更改state的时候,除了更改localStorage,也要更改state,而且更改state才是必须要做的,而更改localStorage只是为了解决刷新页面后数据丢失的问题。(推荐方式,毕竟使用vuex的全套才是正宗)
2. 如果你觉得上面的方案每次都写很麻烦,也可以尝试增加locastorage的监听机制,当发生变化的时候,寻找更改对应的state.
- 拓展符写法
在mapState,mapActions,mapMutations使用的时候,为了不影响正常方法的使用,我们可以使用对象以及数组的拓展方法进行拓展。
methods:{
...mapMutations([
'add','reduce'
]),
...mapActions(['addAction','reduceAction'])
},
// 获取state属性,你也可以采用这种写法
import {mapState} from 'vuex';
computed:mapState({
count:state=>state.count
})
- 单页中直接定义使用(特别说明)
// 如果在模块化构建系统中,请确保在开头调用了 Vue.use(Vuex)
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}
})
//使用的时候 如果想改变数值,必须用事件触发,不是直接改变。
store.commit('increment')
console.log(store.state.count) // -> 1
个人理解
自我实践下来,如果要用好vuex,需要掌握以下的几点:
- 组件数据共享,跨页面数据共享,可以统一管理数据的存储,操作,分发。比如用户数据,比如固定的某些数据是固定某些api获取的且不止用于一个组件或者一个场景;
- 不要形式主义,每个页面都用vuex;每个模块都写getters,actions,常量方法,当你的工程量、数据量达到使用某技术场景的时候,采用某方案会觉得恰如其分;
- 处理基于数据的业务逻辑,一般是跨页面跨组件的,比如购买流程对用户余额,购物车,订单的联动影响;
- vuex核心的index做一些模块公用的存储工具,可以配置一些需要的插件或者工具类;
- 拓展:数据通讯不止vuex,简单的也可以用event bus,甚至页面内的已经能符合你的需求了;