个人笔记-vuex
最近想要沉淀下自己的知识体系,以前光看不记,当时记得,过段时间记忆就模糊了,好脑子不如烂笔头,古人诚不欺我,所以现在决定给用自己的语言方式来给自己记个笔记。
vuex
vuex 有什么好讲的呢,现在的组件通讯有太多方法了,但这种作为vue全家桶里的一员,使用还是蛮广泛的,关于这方面的资料也很多,但我这个笔记是以自己的理解方式用大白话来讲的,有不对的还讲指正。(文章里的代码摘自vuex的中文官网vuex.vuejs.org/zh/)
为什么要用vuex
我们知道,vue是基于组件化的,各管各的,如果这个时候,兄弟组件之间的需要共用某个数据,该怎么通讯呢,方法有很多,比如父子级别的可以通过props和$emit来解决,爷孙级别的可以通过$attrs和$listeners来解决,如果各自相互独立的组件还可能通过中央总线事件来通讯,基本上这三种算是常见的了,当然,也有人说,可以做个全局的变量,然后大家都去拿,在大型项目开发中,这个变量你存了某个值,然后又被另一个人改了,你根本不知道是找哪个人哪行代码改了,这个时候就需要我们进行一个约定,一个特定的地方,保存我们共同的东西,通过约定的方法去更改,以便我们能定位到是谁改了,传了什么东西进来,又方便我们进行追踪。vuex还有一个好处,他里面存的是当前的状态,这就意味着,这里的发生了变化,能响应到对应的组件调用处。
共同的地方-Store
“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。
Vue.use(Vuex) // 重点
const store = new Vuex.Store({
state: {
count: 0
}
})
复制代码
store实例里一定包含一个state对象,里就会存放所有的共有数据状态。
那么组件里面我要去取到这个状态该怎么做呢,首先要注入
const app = new Vue({
el: '#app',
// 把 store 对象提供给 “store” 选项,这可以把 store 的实例注入所有的子组件
store,
components: { Counter },
template: `
<div class="app">
<counter></counter>
</div>
`
})
复制代码
在根组件里注入,这样所属组件就能通过this.$store.state
访问到store实例里的state对象了,其实这种方法在vue里很常见,包括路由啊之类的,都是通过在根组件注入,所属组件调用api的方式来取值的。
Getter
Getter有什么用呢?之前我们不是可以直接通过this.$store.state访问到实例里的state对象了吗,那还要这个有什么用呢,在我看来,Getter有三个作用
- 可以对this.$store.state里的某个属性做计算,比如排序啊,过滤之类的
- 可以直接通过方法访问,对state里的数据进行筛选,比如
store.getters.fun(id)
- 进行映射,这就要用到
mapGetters
这个辅助函数:
mapGetters
import { mapGetters } from 'vuex'
export default {
// ...
computed: {
// 使用对象展开运算符将 getter 混入 computed 对象中
...mapGetters([
'doneTodosCount',
'anotherGetter',
// ...
])
}
}
复制代码
这样写有什么好处呢,这其实是一个映射,可能不同的理解解释的不一样,大家也不用纠结,这样写了之后可以通过this.doneTodosCount
直接拿到,相当于把this.$store.state.doneTodosCount
映射成了this.doneTodosCount
,这样大家就再也不用噼里啪啦写一堆重复的东西了。
Mutation
之前我们提到,我们要约定一个方法去更新state里数据的状态,这个约定的方法就是提交 mutation。
const store = new Vuex.Store({
...
mutations: {
increment (state) {
// 变更状态
state.count++
}
}
})
复制代码
每一个mutation都类似一个事件,increment就是这个事件的type,你不能直接调用这个事件的回调,需要以相应的 type 调用 store.commit 方法:store.commit('increment')
有时候我们要需要对传入的数据进加以第三方数据进行运算,文档里叫提交载荷
// ...
mutations: {
increment (state, n) {
state.count += n
}
}
复制代码
调用
store.commit('increment', 10)
文档里说载荷应该是一个对象,在大多数情况下,载荷应该是一个对象,这样可以包含多个字段并且记录的 mutation 会更易读,这块表示也蒙。。。
// ...
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
复制代码
还有一种对象提交模式,我觉得没啥用,跳过。
mapMutations
同mapGetter,也是映射成this.的写法
import { mapMutations } from 'vuex'
export default {
// ...
methods: {
...mapMutations([
'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`
// `mapMutations` 也支持载荷:
'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)`
]),
...mapMutations({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
})
}
}
复制代码
使用常量替代 Mutation 事件类型
这个看项目吧,大型项目可能需要,各种规范。
敲个黑板: mutation 都是同步事务,什么是同步事务呢,简单的来说就是状态变更都应该在此刻完成,不能有异步的操作,需要加入异步的操作见后面的Action知识点。
Action
Action 类似于 mutation,不同在于:Action 提交的是 mutation,而不是直接变更状态。Action 可以包含任意异步操作。
const store = new Vuex.Store({
...
mutations: {
increment (state) {
state.count++
}
},
actions: {
/* es6解构写法
increment ({ commit }) {
commit('increment')
}*/
increment (context) {
context.commit('increment')
}
}
})
复制代码
这个context是个什么东西呢,文档上面是这么说的:与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters。
Action 通过 store.dispatch 方法触发:
store.dispatch('increment')
,同时Action里支持异步操作,举个简单的例子
actions: {
incrementAsync ({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
}
复制代码
action里的写法基本上和mutationu差不多
// 以载荷形式分发
store.dispatch('incrementAsync', {
amount: 10
})
// 以对象形式分发
store.dispatch({
type: 'incrementAsync',
amount: 10
})
复制代码
mapActions
功能同之前的类似
组合 Action
组合Action,我个人用的不多,但原理要了解一下,Action支持异步,所以在Action里可以写异步的commit,包括Promise,包括多个commit依赖性的先后触发。
Module
大型项目多人会用到的东西,各自管各自的模块。每个模块都有自己对应的一套完整的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 的状态
复制代码
对于module,我觉得只需关注几个点就可了:
模块内部的 mutation 和 getter,接收的第一个参数是模块的局部状态对象,而不是顶层的了。
const moduleA = {
state: { count: 0 },
mutations: {
increment (state) {
// 这里的 `state` 对象是模块的局部状态
state.count++
}
},
actions: {
incrementIfOddOnRootSum ({ state, commit, rootState }) {
if ((state.count + rootState.count) % 2 === 1) {
commit('increment')
}
}
},
getters: {
sumWithRootCount (state, getters, rootState) {
return state.count + rootState.count
}
}
}
复制代码
关于这块,我觉得只需关注几个点就Ok了。
现在的state都指局部的状态对象了,顶级的状态为rootState
const moduleA = {
// ...
getters: {
sumWithRootCount (state, getters, rootState) {
return state.count + rootState.count
}
},
actions: {
incrementIfOddOnRootSum ({ state, commit, rootState }) {
if ((state.count + rootState.count) % 2 === 1) {
commit('increment')
}
}
}
}
复制代码
参数和非module一样,只不过多了第三个参数,这个参数指向顶级状态对象。
命名空间
默认情况下,模块内部的action ,mutation,getter是注册在全局命名空间的–这样使得多个模块能够对同一mutation或者action做出响应;如果希望你的模块更加自包含或者提高可重用性,你可以通过添加namespaced:true 的方式使其成为命名空间模块,当模块被注册后,他的所有getter,action,mutation都会自动根据模块注册的路径调整命名;x