什么是Vuex?
Vuex是专门用在vue的状态管理模式,Vuex通过把组件的状态(数据)统一管理在一棵状态树中,方便不同组件使用共同的数据源和修改数据。
Vue中的单向数据流主要是:
- state,驱动应用的数据源;
- view,以声明方式将 state 映射到视图;
- actions,响应在 view 上的用户输入导致的状态变化。
在没有Vuex的时候,组件之间共享状态只能通过传值,常见的方法如下:
1、父组件传子组件:使用v-bind或者简写:(绑定属性)
//parent.vue
<son :title="post.title"></son>
//son.vue
app.component('son', {
props: {
title: {
type:String,
default:''
}
}
})
2、子组件传父组件:使用v-on或者@(绑定事件)
//parent.vue
<son :value="value" @update:value="event" ></son>
//or parent.vue
<son v-model:value="value"></son>
//组件上的 v-model 使用 value 作为 prop 和 update:value 作为事件
//son.vue
app.component('parent', {
setup(props,{emit}){
const change = ()=> {
emit('update:value','123')
}
return {
change
}
}
})
3、兄弟组件互传:
兄弟组件可以以他们共同的父组件为媒介进行通讯,在简单应用中也是可取的,但是项目大了就难于理解和维护。
4、provide/inject:
Vue3中的新特性,中文翻译为:提供和注入,可以用于夸层级的通信,如下图中A和C组件之间的通信。
//parent.vue 组件A
app.component('parent', {
provide: {
user: 'John Doe'
}
})
//children.vue 组件C
app.component('children', {
inject: ['user'],
})
在复杂的系统中,组件间的通讯相当频繁,单纯通过上面介绍的几种方法可以实现,但是逻辑将会很混乱也不利于维护。Vuex的出现就是为了解决这个问题。多个组件公用一个状态和多个组件改变同一个状态将变得很好维护。
Vuex的五脏六腑
Vuex把所有的状态都储存在store中,只有通过mutation来改变store的状态。如上图Vuex包含五个重要的概念:
1、State:
State是响应式的,所以在视图组件中可以使用computed来重新触发视图的变化,当然也是可以用watch。
//监听或者计算以下的count状态
store.state.count
Vuex还提供了mapState辅助函数,让state状态变为视图中的计算属性变得更容易。
import { mapState } from 'vuex';
export default {
computed: mapState({
// 箭头函数可使代码更简练
count: state => state.count,
// 传字符串参数 'count' 等同于 `state => state.count`
countAlias: 'count',
// 为了能够使用 `this` 获取局部状态,必须使用常规函数
countPlusLocalState (state) {
return state.count + this.localCount
}
})
}
2、Getter:
Getter接受state作为第一个参数,其他的getter作为第二个参数。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
getters: {
doneTodosCount: (state, getters) => {
return getters.doneTodos.length
}
}
3、Mutation:
提交mutation是更改state的唯一方法,和大部分的状态管理框架一样,Vuex也是为了方便追踪状态更改的来源而做的限制。
store.commit('increment')
mutation必须是同步函数,而且mutation也可以提交负载payload。
store.commit('increment', 10)
4、Action:
action通过提交mutation来改变state,这是为了统一记录谁促发了state的改变。
actions: {
increment (context) {
context.commit('increment')
}
}
在视图组件中则用dispatch来分发action。
store.dispatch('increment')
action最重要的作用就是它可以是异步的方法,前面的mutation要求是同步的方法,所以在遇到异步的时候就需要使用action执行完异步任务后,再提交mutation来改变state。
在视图组件中调用异步的action,可以让action返回promise,之后dispatch该action副作用就可以了。
store.dispatch('actionA').then(() => {
// ...
})
5、Module:
const moduleA = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... },
getters: { ... }
}
const moduleB = {
state: () => ({ ... }),
mutations: { ... },
actions: { ... }
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a // -> moduleA 的状态
store.state.b // -> moduleB 的状态
对于大型项目来说,简单的单一状态树确实有时候用起来捉襟见肘,所以vuex引入了模块化思想,允许把store分成不同的模块module。不同的mudole中的mutation和action都是注册在全局的,因此不同模块都可以对同一个action和mutation作出响应,所以vuex引入了命名空间。
namespaced: true,
只要在module中添加namespaced就可以区分开不同模块中的action和mutation,而不通模块之间的相互调用可以通过额外的参数来实现。比如getters中可以使用rootState, rootGetters来访问其他module中的方法和状态等。
getters: {
// 在这个模块的 getter 中,`getters` 被局部化了
// 你可以使用 getter 的第四个参数来调用 `rootGetters`
someGetter (state, getters, rootState, rootGetters) {
getters.someOtherGetter // -> 'foo/someOtherGetter'
rootGetters.someOtherGetter // -> 'someOtherGetter'
}
},
协作分工
再来看看这张图,简单总结一下Vuex的工作流程:
1、Vue Components通过dispatch来分发actions,当然也可以直接提交mutation;
2、actions可以处理异步方法,比如请求接口数据等等,然后提交mutation;
3、mutation改变state中的状态;
4、state中的状态都是响应性的,直接促发视图Vue components的更新,完成一个循环;
5、可见,所有的状态改变都经过mutation,所以所有的状态改变都可以被记录,便于devtools的调试。
你以为结束了吗?
我还想介绍一个挺好用的插件:vuex-persistedstate
Vuex储存的状态只要浏览器一刷新就没了,对于需要持久化的数据来说有点不友好,比如登陆状态等等。
vuex-persistedstate使用浏览器的本地缓存(localStorage)对state进行储存,意味着如果你不主动删除或者更新,它将一直存在你的浏览器中。当然也可以把储存的方式改为sessionStorage后者cookies,只要配置一下就好了。
结合Vuex的模块化使用起来也很方便:
import Vuex from 'vuex';
import columns from './modules/example.js';
import createPersistedState from 'vuex-persistedstate';
export default Vuex.createStore({
strict: true,
state: {},
getters: {},
actions: {},
mutations: {},
modules: {
columns,
},
plugins: [
createPersistedState({
key: 'setting', //储存状态的key,也可以不配置
paths: ['example'], //example.js和vuex中普通的模块写法都一样
}),
],
});
参考:
微信公众号,更好的交流!