一、Vuex是什么?
Vuex是一个专为vue.js应用程序开发的状态管理模式。他采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化
二、什么是状态管理模式?
让我们从一个简单的vue计数应用开始
new Vue({
//state
data(){
return {
count:0
}
},
//view
template:`
<div>{{count}}</div>
`,
//actions
methods:{
increment(){
this.count++
}
}
})这个状态自管理应用包含以下几个部分:
(1)state,驱动应用的数据源
(2)view,以声明方式将state映射到视图
(3)actions,响应在view上的用户输入导致的状态变化
以下是一个表示单向数据流理念的示意图:

但是,当我们的应用遇到多个组件共享状态时,单向数据流的简洁性很容易被破坏:
(1)多个视图依赖同一个状态(传参的方法对于多层嵌套的组件非常繁琐,对于兄弟组件的间的状态传递无能为力)
(2)来自不同视图的行为需要变更同一状态()
三、开始
每一个vuex应用的核心就是store(仓库)。store基本上就是一个容器,他包含着你的应用中大部分的状态。vuex和单纯的全局对象有以下两点不同:
1.vuex的状态存储是响应式的。当vue组件从store中读取状态的时候,若store中的状态发生变化,那么相应的组件也会相应地得到高效更新
2.你不能直接改变store中的状态,改变store中的状态的唯一途径就是显示的提交(commit)mutation。这样使得我们可以方便的跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地理解我们的应用。
最简单的store
const store = new Vuex.Store({
state:{
count:0
},
mutations:{
increment(){
state.count++
}
}
})现在,你可以通过store.state来获取状态对象,以及通过store.commit方法触发状态变更:
store.commit("increment")
console.log(store.state.count)//1四、核心概念
state
1.单一状态树
Vuex使用单一状态树,用一个对象就包含了全部的应用层级状态。每个应用仅仅包含一个store实例
2.如何在vue组件中获得vuex状态?
由于vuex的状态存储是响应式的,从store实例中读取状态最简单的方法就是在计算属性中返回某个状态:
const Conter = {
template:`<div>{{count}}</div>`,
computed:{
count(){
return store.state.count
}
}
}每当store.state.count变化的时候,都会重新求取计算属性,并且出发更新相关联的DOM.
然而,这种模式导致组件依赖全局状态单例,在模块化的构建中,每个需要使用state的组件中需要频繁的导入,因此Vuex通过store选项,提供一种机制将状态从根组件注入到每一个子组件中
const app = new Vue({
el:"#app",
//把store对象提供给store选项,这可以把store的实例注入所有的子组件
store,
components:{Counter},
template:`<div class="app">
<counter></counter>
</div>
`
})通过在根实例中注入store选项,该store实例会注入到根组件下的所有子组件,且子组件可以通过this.$store访问到
const Counter = {
template :`<div>{{count}}</div>`,
computed:{
count(){
return this.$store.state.count
}
}
}3.什么是mapState辅助函数?做什么用的?当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余,mapState辅助函数就是为了解决这个问题,该辅助函数可以帮我我们生成计算属性,让你少按几次键:
import { mapState } from 'vuex'
export default {
computed: mapState({
count:state => state.count,
countAlias:'count',
countPlusLocalState(state){
return state.count + this.localCount
}
})
}当映射的计算属性的名称与state的子节点名称相同时,我们也可以给mapState传一个字符串数组
computed:mapState([
//映射this.count为store.state.count
'count'
])Getter
1.什么getter?做什么用的?怎么用?
getter相当于store的计算属性,getter的返回值会根据它的依赖被缓存起来,且只有当他的依赖值发生变化时才会被重新计算
有时候我们需要从state中派生一些状态,例如对列表进行过滤
Getter接受state作为其第一个参数:
const store = new Vuex.Store({
state:{
todos:[
{id:1,text:'111',done:true},
{id:2,text:'222',done:false}
]
},
getters:{
doneTodos:state => {
return state.todos.filter(todo => todo.done)
}
}
})2.如何通过属性访问?
Getter会暴露为store.getters对象,你可以以属性的形式访问这些值:
store.getters.doneTodosGetter也可以接受其他getter作为第二个参数
getters:{
doneTodoCount: (state,getters) => {
return getters.doneTodos.length
}
}
store.getters.doneTodosCount 我们可以很容易在任何组件使用它:
computed:{
doneTodosCount(){
return this.$store.getters.doneTodosCount
}
}注意:getter在通过属性访问时是可以作为vue的响应式系统的一部分缓存其中的3.如何通过方法访问
你也可以通过getter返回一个函数,来实现给getter传参,在你对store里的数组进行查询时非常有用
getters:{
getTodoById:(state)=>(id){
return state.todos.find(todo => todo.id => id)
}
}4.mapGetters辅助函数是做什么用的?怎么用?
mapGetters辅助函数仅仅是将store中的getter映射到局部计算属性:
import { mapGetters } from 'vuex'
export default {
computed:{
...mapGetters([
'doneTodosCount',
'anotherGetter'
])
}
}Mutation1.Mutation的用处?怎么用?
Mutation是更改vuex的store中的状态的唯一方法。用于更改store状态,类似于事件:首先在store中加入选项,每个mutation都有一个字符串的事件类型和一个回调函数(handler)。这个函数就是我们实际更改状态的地方,默认传入state作为参数:
const store = new Vuex.Store({
state: {
count: 1
},
mutation:{
increment(state){
state.count++ //变更状态
}
}
})你不能直接调用一个mutation handler。这个选项更像是事件注册:当触发一个类型为increment的mutation时,调用此函数。要唤醒一个mutation handler,你需要以相应的type调用store.commit方法
store.commit("increment")2.什么是提交载荷(payload)?怎么用?
提交载荷是向store.commit传入的一个额外参数,可以是字符串,也可以是对象(大多数情况为对象)
mutations: {//在store中注入mutations选项,并注册increment方法 increment: (state, payload) { state.count += payload.amount }}store.commit('increment', {//子组件中提交 amout:10})3.对象风格的提交方式
store.commit({//此处整个对象都作为载荷传给mutation函数
type:"increment",
amount:10
})4.Mutation需遵守Vue的响应规则
既然vuex中store的状态是响应式的,那么当我们变更状态时,监视状态的vue组件也会自动更新
(1)最好提前在你的store中初始化好所有所需属性
(2)当需要在对象上添加新属性时,可以使用
Vue.set(obj,"newProp",123)5.使用常量替代Mutation事件类型?
把常量放在单独的文件中
//mutation-types.js
export const SOME_MUTATION = 'some_mutation'
//store.js
import Vuex from 'vuex'
import {SOME_MUTITION} from './mutation-types'
const store = new Vuex.Store({
state:{...},
mutations: {
[SOME_MUTATION] (state) {
}
}
})6.Mutation必须是同步函数
7.如何在组件中提交Mutation?
(1)在组件中使用this.$store.commit("xxx")提交
(2)使用mapMutation辅助函数将组件中的methods映射为store.commit调用
import { mapMutations } from 'vuex'
export default {
methods: {
...mapMutations([
'increment',
'incrementBy',
add: 'increment'
])
}
}Action
1.Action类似于mutation,不同在于:
(1)action提交的是mutation,而不是直接变更状态
(2)action可以包含任意异步操作
const store = new Vue.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})Action函数接受一个与store实例具有相同属性和方法的context对象,因此你可以调用context.commit提交一个mutation,或者通过context.state和context.getters来获取state和getters
2.如何分发action
action通过store.dispatch方法触发:
store.dispatch('increment')我们可以在store内部执行异步操作
actions: {
incrementAsync ({commit}) {
setTimeout(() => {
commit('increment')
},1000)
}
}actions支持同样的载荷方法和对象方式进行分发:
//以载荷形式分发
store.dispatch('incrementAsync',{
count: 0
])
//以对象形式分发
store.dispatch({
type:"incrementAsync",
count:10
})3.如何在组件中分发action
(1)使用this.$store.dispatch("xxx")分发
(2)使用mapActions辅助函数将组件的methods映射到store.dispatch
import {mapActions} from "vuex"
export default {
methods: {
...mapActions([
'increment',
'incrementBy'
])
}
}4.什么是组合action?如何组合action?
action通常是异步的,store.dispatch可以处理被触发的action的处理函数返回的promise,并且store。dispatch仍旧返回promise
actions: {
actionA({commit}) {
return new Promise(() => {
setTimeout(() => {
commit('someMutation')
resolve()
},1000)
})
}
}现在你可以:
store.dispatch('actionA').then(() => {
//...
})在另一个action中也可以:
actions: {
actionB({dispatch, commit}){
return dispatch('actionA').then(() => {
commit('someOtherMutation')
})
}
}Module
1.module作何用?怎么用?
由于使用单一状态树,应用所有状态集中到一个对象,当应用非常复杂时,store对象变的非常臃肿,为了解决这个问题,我们将store分割成module(模块)
const moduleA = {
state: {...},
mutations: {...},
actions: {...},
getters: {...},
}
const moduleB = {
state: {...},
mutations: {...},
actions: {...},
getters: {...},
}
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
store.state.a
store.state.b2.什么是模块的局部状态?
模块的局部状态即state
对于模块内部的mutation和getter,接收的第一个参数是模块的局部状态
const moduleA = {
mutations: {
increment (state) {//state就是模块的局部状态
state.count++
}
},
getters: {
doubleCount (state) {
return state.count*2
}
}
}同样,对于模块内部的action,局部状态通过context.state暴露出来,根节点状态则为context.rootstate:
const moduleA = {
actions: {
incrementIfOddOnRootSum ({state,commit,rootstate}) {
if((state.count + rootState.count %2 ===1)){
commit('increment')
}
}
}
}对于模块内部的getter,根节点状态会作为第三个参数暴露出来
const moduleA = {
getters: {
sumWithRootCount (state,commit,rootState) {
return state.count + rootState.count
}
}
}3.命名空间
默认情况下,模块内部的action,mutation,getter,是注册在全局命名空间的,这样使得多个模块能够同一mutation或action做出响应
如果你希望你的模块具有更高的封装度和复用性,你可以通过添加namespaced:true的方式使其成为带命名空间的模块。当模块被注册后,他的getter,action及mutation都会自动根据模块注册的路径调整命名
const store = new Vue.Store({
modules: {
account: {
namespaced: true,
state:{},
getters:{},
modules: {
myPage: {
state:{}
}
}
}
}
})4.在带命名空间的模块内访问全局内容(global assets)
如果你希望使用全局state和getter,rootState和rootGetter会作为第三和第四参数传入getter,若需要在全局命名空间内分发action或提交mutation,将{root:true}作为第三参数传给dispatch或commit即可
modules: {
foo: {
namespaced: true,
getters: {
someGetter (state,getters,rootState,rootGetter) {
getters.someOtherGetter //foo/someOtherGetter
rootGetter.someOtherGetter //someOtherGetter
},
someOtherGetter: state => {...}
},
actions: {
someAction ({dispatch,commit,getters,rootGetters}) {
getters.someGetter //foo/someGetter
rootGetters.someGetter //someGetter
dispatch('someOtherAction')//foo/someOtherAction
dispatch('someOtherAction',null,{root:true})
commit('someMutation')//foo/someMutation
commit('someMutation',null,{ root: true})
}
}
}
}5.如何在带命名空间的模块注册全局action?能解决什么问题?
若需要在带命名空间的模块注册全局action,你可添加root:true,并将这个action的定义放在函数handler中
modules: {
foo: {
namespaced: true,
action: {
someAction: {
root: true,
handler (namespaceContext, payload) {
}
}
}
}
}6.带命名空间的绑定函数
本文介绍Vue.js应用程序的状态管理模式Vuex,包括其基本概念、核心功能和使用方法。Vuex通过集中式存储管理组件状态,确保状态变化的可预测性。
3691

被折叠的 条评论
为什么被折叠?



