前言
本篇文章将为大家介绍什么是vuex?什么时候使用?该如何使用?vuex的装饰器是什么?
为什么要使用vuex
1、当多个视图依赖同一状态(state--->view)
2、来自不同视图的行为需要变更同一状态(view--通过Actions-->state)
第一种情况的理解:当组件嵌套,可以使用组件传参的方法传递数据,但当多层嵌套时,传参方法this.$emit;prop会变得非常繁琐且不利于维护。
第二种情况的理解:当多个组件的行为(提交表单、下一步),需要更改同一个数据时,父子组件可以直接引用(this.$ref;this.$parent)触发对应的方法并修改数据。但当视图嵌套过于复杂的时候,以上的模式会导致代码难以维护。
所以可以将用到的公共状态提取出来,统一管理。把组件树抽象为一个视图,无论在组件树的任何位置,都可以获取和更改数据。
vuex的基本概念
每个应用仅包含一个store实例,实例中可传入参数(object)state、getters、mutationss、actions、modules
state
抽取的公共状态就存在这里
const store = new Vuex.Store({
state: {
// 定义一个name,以供全局使用
name: '张三',
// 定义一个number,以供全局使用
number: 0,
// 定义一个list,以供全局使用
list: [ { id: 1, name: '111' }, { id: 2, name: '222' }, { id: 3, name: '333' }, ]
}
});
通常在组件中的计算属性获取state
computed:{
// 通过return获取state数据
name(){
return this.$store.state.name
}
// 当state数据较多时,可以使用mapState辅助函数
...mapState(['name']),
// 赋值别名,接收对象
...mapState({aliasName: 'name'}),
}
getters
有时需要从state中派生出一些状态,例如数据改造、数组过滤等
简单理解:store中的getters相当于组件中的computed,写法一致
getters: {
longName (state) {
return 'stateGetters' + state.name
},
getTodoByid: (state) => (id) => {
return state.todos.find(todo => todo.id === id)
}
},
getLongName () {
return this.$store.getters.longName
},
// 同样可以使用展开运算符
...mapGetters(['longName']),
...mapGetters({aliasLongName: 'longName'}),
// 可对getters传入参数,getter通过返回函数来获取该参数
getTodoByid () {
return this.$store.getters.getTodoByid(2)
}
mutations
以上讲的是获取state,那么如何更改state数据呢?
唯一的方法就是提交mutation。
但是不能直接操作mutation,需要调用store.commit方法。mutation可接收两个参数(state, 'payload'),
// ...
mutations: {
increment (state) {
state.count++
},
setMyNumber (state, obj) {
state.count += obj.num
},
aNameMutation (state, dataName) {
state.name = dataName
}
}
methods: {
changeState () {
this.$store.commit('setMyNumber', {num: 6})
},
// 可以使用mapMutations,映射为this.$store.commit('aNameMutation', '传入参数')
...mapMutations(['aNameMutation']),
// 可以起别名,映射为this.$store.commit('aNameMutation')
...mapMutations({add: 'aNameMutation'})
}
切记mutation中必须使用同步方法,因为每一条 mutation 被记录,devtools 都需要捕捉到前一状态和后一状态的快照。执意写异步也能执行,但后一状态无法被记录。
actions
需要异步操作可以在actions中写。
Actions 和 Mutations 区别
- Actions 提交的的mutation,而mutation提交的是state
- Actions可以包含任何异步操作
actions: {
selfF (context) {
console.log(context)
setTimeout(() => {
context.commit('increment')
}, 1000)
},
ActionA ({commit}, obj) {
setTimeout(() => {
commit('setMyNumber', obj)
}, 1000)
}
}
Actions拥有store实例同样的属性(但不是store的实例),第一个参数context(包含commit、dispatch、getters、state);第二个参数是载荷(传入参数)
和mutation一样,可以使用mapActions辅助函数。
// 在组件中使用
// 提交actions
touchActions () {
this.$store.dispatch('selfF')
},
// 通过mapActions分发
...mapActions(['ActionA']),
touchActionsA () {
this.ActionA({num: 10})
}
因为actions是异步的,并且返回一个promise,所以想要知道action什么时候结束,可以使用.then().catch()来写回调和捕获错误。
modules
当应用越来越复杂时,store会越来越臃肿。所以可以将store分割成模块,每个模块拥有自己的state/getters/mutations/actions,也可以再次嵌套modules。
使用方法:
在index.js统计目录下新建modules文件夹,新增moduleA.js(模块A)
const state = () => ({
moduleAName: '我是模块a的数据',
work: '工人'
})
// getters
const getters = {
deriveName (state) {
// state是moduleA中的state
return state.moduleAName + '---派生字符串'
}
}
// actions
const actions = {
moduleANameAction (context, obj) {
context.commit('moduleANameMutation', obj)
}
}
// mutations
const mutations = {
moduleANameMutation (state, obj) {
state.moduleAName += obj.string
}
}
export default {
namespaced: true,
state,
getters,
actions,
mutations
}
在index.js中引入
import moduleA from './modules/moduleA'
const store = new Vuex.Store({
// ...
modules: {
moduleA
}
})
基础用法(问题):
1、为什么要使用模块?如何使用模块?
为了避免store入口文件过于臃肿和方便管理。新建模块js文件,分别定义state、getters、mutations、actions后暴露出去,并在index.js中modules挂载。
2、在组件中如何使用模块中定义的state、getters、mutations、actions?
若未定义命名空间,直接调用即可。
this.$store.state.xxx
this.$store.getter['type']
this.$store.commit('type')
this.$store.dispatch('type')
3、什么是模块的命名空间?如何使用?使用后有何不同?
默认情况下模块的getters、mutations、actions是注册在全局的,为了避免同名、使组件有更高的复用性封装度,所以使用命名空间。
在模块内添加nameSpaced:true
模块使用命名空间后,写法并没有改变,只是添加了一个属性nameSpaced:true 。不同的地方在组建内用的模块的getter、mutation、action需要按路径匹配
this.$store.getter['moduleA/type']
this.$store.commit['moduleA/type']
this.$store.dispatch['moduleA/type']
4、如何访问全局state、getter?
rootState、rootGetter会作为第三个、第四个参数传入getter。同时也会传入action的context参数对象。
getters: {
// ...
someGetter (state, getters, rootState, rootGetters) {
// ...
}
}
actions:{
someAction ({ dispatch, commit, getters, rootState, rootGetters }) {
// ...
}
}
5、 如何在模块内访问全局的mutations、actions?
将{root:true}传入commit和dispatch
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'
},
6、如何在模块内注册全局的muataion、actions?
actions: {
someAction: {
root: true,
handler (namespacedContext, payload) { ... } // -> 'someAction'
}
}
mutation和action的区别
- mutation提交的是state;action提交的是mutation
- mutation只支持同步方法,action支持任何异步写法
装饰器vuex-module-decorators
vuex模块的装饰器,内容同样包含state、getters、mutations、actions,只是写法略有区别。装饰器的主要作用是在组件中帮助用户简便写法,无需this.$sotre.diapatch('namespace/name')写这么一大串了,当然这种写法仍然有效。
定义模块
import { Module, VuexModule, Mutation, Action, getModule } from 'vuex-module-decorators'
import store from '@/store'
type User = {name: string; password: string}
// 动态模块,只有组件内使用的时候才会注册
@Module({name: 'moduleB', namespaced: true, dynamic: true, store: store})
class moduleB extends VuexModule {
// state
public loginInfo: User[] = []
// getter
get userNumber():number {
return this.loginInfo.length
}
// mutation
@Mutation
USERINFO(user: User): void {
console.log(user)
this.loginInfo.push(user)
}
// action
// commit两种调用方式:1、在Action后边添加commit,最后return的值即为传入USERINFO的参数
@Action({ commit: 'USERINFO' })
ADD_USER_ONE(): User {
return { name: '张三', password: '123' }
}
// 2、在action中通过this.XXX(mutation名称)调用并传值
@Action
ADD_USER_TWO(obj: any): void {
console.log(obj)
this.context.commit('USERINFO', obj)
// this.USERINFO(obj)
}
}
export const moduleBModule = getModule(moduleB)
在组件内使用
// computed
get userinfo() {
return JSON.stringify(moduleBModule.loginInfo)
}
mounted() {
console.log(this.$store)
console.log(moduleBModule)
}
// 提交mutation
commitMutation() {
moduleBModule.USERINFO({name: '奥特曼', password: '666'})
}
// 提交action不传参
commitAction() {
moduleBModule.ADD_USER_ONE()
}
// 提交action传参
commitAction_Two() {
moduleBModule.ADD_USER_TWO({name: '迪加', password: '23333'})
}