作为一个全沾攻城狮,需要会的东西实在太多了。虽然思路都大同小异,但是对于不同的框架,架构和使用的api都有着不小的区别,还是需要花费点时间学习一下,今天要记录的是便是管理vue状态的框架vuex。
vuex是什么 vuex
是一个状态管理框架,也可以叫做数据共享框架。它能够创建一个独立于组件之外的一个共享数据仓储,将需要的数据放在store当中,当需要用到数据的组件通过this.$store.state.xxx获取 。
不使用vuex的现状 父向子传值: v-bind,子组件通过props接收。
子向父传值: $emit()发射自定义事件, 父组件用v-on监听事件。
不相干组件传值,eventbus实现。 接收方用$on,传递方用$emit()
此种方式只能在小型项目中使用,当需要大量组件需要传值时,会通过其他不相干的组件层层传递,导致组件之间耦合严重,维护相当吃力。
使用vuex有什么好处 vuex创建的state是独立于组件的,能够解藕组件。
集中管理数据,易于维护。
共享数据,存储和获取都非常便捷。
state中的数据都是响应式的,引用state的组件能够实时同步UI
存储原则:一般来说将需要共享的数据放在state中,私有的数据放在组件的data中。但是也可以将所有数据都放在state中,这要看个人和项目组的的开发习惯。
vuex组成部分 解构注意事项: state和getter本质上是属性,所以解构的时候是放在computed中,actions和mutactions是方法,所以解构是要放在methods中。
state(公共数据源,需要共享的数据都放在这里面) 使用的时候: this.$store.state.count (this可以省略)
或者使用mapState解构为computed属性,使用哪种方法都可以
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<template >
<div >
{{count}}
</div >
</template >
<script >
import { mapState } from 'vuex'
export default {
computed: {
...mapState([
'count'
])
}
}
</script >
mutations(同步的修改state,mutations中不能使用异步操作) 用setTimeoutt等异步操作会出错 ,异步操作需要放在action中
好处:在统一的地方对应,方便管理和维护
1
2
3
4
5
6
add (state) {
state.count++
},
addN (state, n) {
state.count += n
}
使用时
第一种
this.$store.commit('add')
如果想传递参数
this.$store.commit('addN',3)
第二种mapMutations (注意要放在methods
中)
1
2
3
import { mapMutations } from 'vuex'
/// 省略其他内容
methods: { ...mapMutations([ 'add' , 'addN' ]) }
actions(异步的修改state,它不能直接操作state,需要调用mucations中的方法来操作) 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
mutations: {
add (state) {
state.count++
},
addN (state, n) {
state.count += n
}
},
actions: {
addAsync (context) {
setTimeout(() => {
context.commit('add' )
}, 1000 )
},
addNAsync (context,n) {
setTimeout(() => {
context.commit('addN' ,n)
}, 1000 )
}
},
使用时:方式一
写一个方法,通过$store.dispatch这个action,想要传参数和mucation中写法一样
1
2
3
4
5
6
7
8
9
methods: {
handleAsyncAdd () {
this .$store.dispatch('addAsync' )
},
handleAsyncAddN () {
this .$store.dispatch('addNAsync' ,4 )
}
}
第二种使用方法,解构出来直接使用(注意要放在methods
中)
1
2
3
4
5
6
7
8
methods: {
...mapActions([
'addAsync' ,
'addNAsync'
]),
}
< div @click= "addAsync" >
getters(用于对store中的数据进行加工形成新的数据,但不会影响store中的数据) store的数据变化之后,getter中的数据也会变化
1
2
3
4
5
getters: {
showNum (state) {
return `当前最新的数量 ${ state.count} `
}
},
使用时
1
2
3
<div @click = "addAsync" >
{{$store.getters.showNum}}
</div >
第二种使用方法(注意要放在computed
中)
1
2
3
4
5
computed: {
...mapGetters([
'showNum'
])
}
使用时
1
2
3
<div @click = "addAsync" >
{{showNum}}
</div >
modules(对store进行模块划分) 使用场景:当项目组中有多个人同时开发时,如果放在一个文件里面则会出现修改冲突,这里需要使用modules进行文件切分再组合。
目录结构:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import users from './users'
import todos from './todos'
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
users,
todos
}
})
1
2
3
4
5
6
7
8
9
10
11
12
13
// /store/todos/index.js
import todoStates from './todoStates'
import todoActions from './todoActions'
import todoMutations from './todoMutations'
import todoGetters from './todoGetters'
export default {
namespaced: true ,
state: todoStates,
mutations: todoMutations,
actions: todoActions,
getters: todoGetters
}
1
2
3
4
5
6
7
8
9
10
11
12
13
// /store/users.js
import userStates from './userStates'
import userActions from './userActions'
import userMutations from './userMutations'
import userGetters from './userGetters'
export default {
namespaced: true ,
state: userStates,
mutations: userMutations,
actions: userActions,
getters: userGetters
}
使用的时候,解构的时候写法有区别。commit的时候需要加上模块名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<template >
<div >
<button @click = "addTodo" >add</button >
<li v-for = "todo in todoList" :key = "todo.name" >
{{todo.name}}
</li >
</div >
</template >
<script >
import { mapState, mapMutations } from 'vuex'
export default {
computed: {
...mapState({
todoList: state => state.todos.todoList
})
},
methods: {
...mapMutations({
addTodo (commit) {
commit('todos/addTodo' , 'test' )
}
})
}
}
</script >
使用注解方式 只有使用ts的环境才能使用注解方式,通过调研有以下两种方案。至于怎么选择,看实际项目的情况下,有的时候不一定有发言权,leader定哪个就用哪个吧。
vuex-module-decorators 用法 yarn add vuex-module-decorators
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import { Module, VuexModule, Mutation, Action } from 'vuex-module-decorators'
@Module
export default class Counter2 extends VuexModule {
count = 0
@Mutation
increment(delta: number ) {
this .count += delta
}
@Mutation
decrement(delta: number ) {
this .count -= delta
}
// action 'incr' commits mutation 'increment' when done with return value as payload
@Action ({ commit: 'increment' })
incr() {
return 5
}
// action 'decr' commits mutation 'decrement' when done with return value as payload
@Action ({ commit: 'decrement' })
decr() {
return 5
}
}
vuex-class用法 yarn add vuex-class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import Vue from 'vue'
import Component from 'vue-class-component'
import {
State,
Getter,
Action,
Mutation,
namespace
} from 'vuex-class'
const someModule = namespace ('path/to/module' )
@Component
export class MyComp extends Vue {
@State ('foo' ) stateFoo
@State (state => state.bar) stateBar
@Getter ('foo' ) getterFoo
@Action ('foo' ) actionFoo
@Mutation ('foo' ) mutationFoo
@someModule .Getter('foo' ) module GetterFoo
// If the argument is omitted, use the property name
// for each state/getter/action/mutation type
@State foo
@Getter bar
@Action baz
@Mutation qux
created () {
this .stateFoo // -> store.state.foo
this .stateBar // -> store.state.bar
this .getterFoo // -> store.getters.foo
this .actionFoo({ value: true }) // -> store.dispatch('foo', { value: true })
this .mutationFoo({ value: true }) // -> store.commit('foo', { value: true })
this .module GetterFoo // -> store.getters['path/to/module/foo']
}
}
参考文章:
Vue & TypeScript 初体验 - 使用Vuex (vuex-module-decorators)