vuex适合用于多组件共享数据的情况,多个组件都能读,也都可以改。
vuex的执行流程:
vuex的3个组成部分:action、mutation、state被封装在一个叫store的对象中,store负责管理内部各部分的互相调用,包装流程
- store的dispatch被调用,根据key值匹配调用action的指定方法
- action执行同步/异步收集数据的操作,把结果通过commit发送给指定mutation
- mutation调用指定方法执行state(即数据)更新行为
- 数据被更新后,就是vue实例开始接力了:触发了vue实例的数据代理,然后就是重新解析模板、更新dom
安装
npm install vuex --save
使用
vuex是一个插件,所以需要Vue.use
import Vuex from 'vuex'
Vue.use(Vuex)
注册了vuex,我们就可以在vue里配置store了
const store = Vuex.Store({
state: {
count: 0,
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false }
]
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
},
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
}
}
})
new Vue({
store,
render: h => h(App)
}).$mount('#app')
getters
这里多了个配置getters,他可以看作是基于state的computed属性。使用的方式基本如下:
- 直接$store.getters.xxx获取
- 或者通过mapGetters,本质就是把getter属性通过mixin映射到了vc组件内部,这种方式的好处是可以像本地computed属性一样直接获取,用起来更简介。
类似的还有mapState、mapActions、mapMutationscomputed: { ...mapGetters([ 'doneTodosCount', 'anotherGetter', // ... ]) }
同时getters还可以配置成函数,方便我们动态传参,满足一些复杂一点的场景
getters: {
// ...
getTodoById: (state) => (id) => {
return state.todos.find(todo => todo.id === id)
}
}
调用
$store.getters.getTodoById(2)
模块化store
随着项目迭代,需求的扩展,你的store会变得越来越臃肿,建议模块化拆分store
以上就是一些对象的拼装,但是模块要放到名为modules的属性内部,store的最终结构大致如下:
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 的状态
模块的命名空间
默认模块的state、getter等属性都会合并到根级别,如果你希望模块有更好的封装性,避免多模块命名污染,可以配置上命名空间属性namespace:true
const store = new Vuex.Store({
modules: {
account: {
namespaced: true,
// 模块内容(module assets)
state: () => ({ ... }), // 模块内的状态已经是嵌套的了,使用 `namespaced` 属性不会对其产生影响
getters: {
isAdmin () { ... } // -> getters['account/isAdmin']
},
actions: {
login () { ... } // -> dispatch('account/login')
},
mutations: {
login () { ... } // -> commit('account/login')
},
// 嵌套模块
modules: {
// 继承父模块的命名空间
myPage: {
state: () => ({ ... }),
getters: {
profile () { ... } // -> getters['account/profile']
}
},
// 进一步嵌套命名空间
posts: {
namespaced: true,
state: () => ({ ... }),
getters: {
popular () { ... } // -> getters['account/posts/popular']
}
}
}
}
}
})
使用了模块命名空间后,action和getter内部常规用法只能访问本命名空间的dispatch,commit或者getter,如果要访问全局命名空间,需要使用额外的参数和配置:
modules: {
foo: {
namespaced: true,
getters: {
// 在这个模块的 getter 中,`getters` 被局部化了
// 你可以使用 getter 的第四个参数来调用 `rootGetters`
someGetter (state, getters, rootState, rootGetters) {
getters.someOtherGetter // -> 'foo/someOtherGetter'
rootGetters.someOtherGetter // -> 'someOtherGetter'
},
someOtherGetter: state => { ... }
},
actions: {
// 在这个模块中, dispatch 和 commit 也被局部化了
// 他们可以接受 `root` 属性以访问根 dispatch 或 commit
someAction ({ dispatch, commit, getters, rootGetters }) {
getters.someGetter // -> 'foo/someGetter'
rootGetters.someGetter // -> 'someGetter'
dispatch('someOtherAction') // -> 'foo/someOtherAction'
dispatch('someOtherAction', null, { root: true }) // -> 'someOtherAction'
commit('someMutation') // -> 'foo/someMutation'
commit('someMutation', null, { root: true }) // -> 'someMutation'
},
someOtherAction (ctx, payload) { ... }
}
}
}
模块动态注册
在 store 创建之后,你可以使用 $store.registerModule 方法注册模块
如果大多场景下,你的模块化store并不会被使用,只在你的业务流程页面中被用到,这时可以考虑在合适的生命周期节点(比如created)动态注册你的模块。同时要注意先检查模块是否已经被安装:$store.hasModule(moduleName),避免重复注册模块,不然会出现逻辑问题。你也可以卸载自己的模块:$store.unregisterModule(moduleName)
// 注册模块 `myModule` 通过store.state.myModule访问
$store.registerModule('myModule', {
// ...
})
// 注册嵌套模块 `nested/myModule` 通过store.state.nested.myModule访问
$store.registerModule(['nested', 'myModule'], {
// ...
})
网友评论