VUE Study Day14 - vuex
# state
# 定义
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 使用
store.commit('increment') //触发mutations的increment更新
console.log(store.state.count) // -> 1 ,调用
2
3
# 全局调用
Vuex 通过 store
选项,提供了一种机制将状态从根组件“注入”到每一个子组件中(需调用 Vue.use(Vuex)
):
const app = new Vue({
el: '#app',
// 把 store 对象提供给 “store” 选项,这可以把 store 的实例注入所有的子组件
store,
components: { Counter },
template: `
<div class="app">
<counter></counter>
</div>
`
})
2
3
4
5
6
7
8
9
10
11
通过在根实例中注册 store
选项,该 store 实例会注入到根组件下的所有子组件中,且子组件能通过 this.$store
访问到。让我们更新下 Counter
的实现:
const Counter = {
template: `<div>{{ count }}</div>`,
computed: {
count () {
return this.$store.state.count
}
}
}
2
3
4
5
6
7
8
# mapState(常用)
mapState作用:同时将多个状态声明为计算属性
// 当计算属性名称与state子节点名称相同时,可以直接传字符串数组
computed: {
...mapState("d2admin/user", [
"info"
]),
},
2
3
4
5
6
当state有多个modules时,可以按需导入
computed: { ...mapState("d2admin/user", [ "info" ]), }, 比如2层结构: ------------------------------- import d2admin from './modules/d2admin' //首先向store暴露d2admin export default new Vuex.Store({ modules: { d2admin, } }) cat ./modules/d2admin/modules/user.js // 其次d2admin的子modules包含user.js,里面包含info属性 export default { namespaced: true, state: { // 用户信息 info: {} }, } // 所以层级为"d2admin/user",最后得到一个同名的this.info(映射为)
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
# getters
通俗讲就是对state的加工,如同计算属性,举个例子:
computed: {
doneTodosCount () {
return this.$store.state.todos.filter(todo => todo.done).length
}
}
2
3
4
5
如果有多个组件需要用到此属性,我们要么复制这个函数,或者抽取到一个共享函数然后在多处导入它——无论哪种方式都不是很理想。
# getters语法
const store = new Vuex.Store({
state: {
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false }
]
},
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
}
}
})
2
3
4
5
6
7
8
9
10
11
12
13
Getter 也可以接受其他 getter 作为第二个参数:
getters: {
// ...
doneTodosCount: (state, getters) => {
return getters.doneTodos.length
}
}
2
3
4
5
6
# 访问getters
属性访问
此时有缓存
store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }]`
// 在计算属性中调用
computed: {
doneTodosCount () {
return this.$store.getters.doneTodosCount
}
}
2
3
4
5
6
通过方法访问
此时无缓存
你也可以通过让 getter 返回一个函数,来实现给 getter 传参。在你对 store 里的数组进行查询时非常有用。
getters: {
// ...
getTodoById: (state) => (id) => {
return state.todos.find(todo => todo.id === id)
}
}
store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false }
2
3
4
5
6
7
# mapGetters
辅助函数
mapGetters
辅助函数仅仅是将 store 中的 getter 映射到局部计算属性:
import { mapGetters } from 'vuex'
export default {
// ...
computed: {
// 使用对象展开运算符将 getter 混入 computed 对象中
...mapGetters([
'doneTodosCount',
'anotherGetter',
// ...
])
}
}
2
3
4
5
6
7
8
9
10
11
12
13
如果你想将一个 getter 属性另取一个名字,使用对象形式:
...mapGetters({
// 把 `this.doneCount` 映射为 `this.$store.getters.doneTodosCount`
doneCount: 'doneTodosCount'
})
2
3
4
# Mutations
mutations必须是同步函数,否则将无法追踪。
state不能直接修改,需要定义相应的mutations来调用。如:
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
increment (state) {
// 变更状态
state.count++
}
}
})
// 调用
store.commit('increment')
2
3
4
5
6
7
8
9
10
11
12
13
14
# payload
调用时可以传参,如:
mutations: {
increment (state, n) {
state.count += n
}
}
store.commit('increment', 10)
2
3
4
5
6
以对象形式传参
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
store.commit('increment', {
amount: 10
})
2
3
4
5
6
7
8
# 以对象形式调用mutations
提交 mutation 的另一种方式是直接使用包含 type
属性的对象:
store.commit({
type: 'increment',
amount: 10
})
2
3
4
当使用对象风格的提交方式,整个对象都作为载荷传给 mutation 函数,因此 handler 保持不变:
mutations: {
increment (state, payload) {
state.count += payload.amount
}
}
2
3
4
5
# 响应式更新
注意事项:
最好提前在你的 store 中初始化好所有所需属性。
当需要在对象上添加新属性时,你应该
使用
Vue.set(obj, 'newProp', 123)
, 或者以新对象替换老对象。例如,利用对象展开运算符 (opens new window) (opens new window)我们可以这样写:
state.obj = { ...state.obj, newProp: 123 }
# mapMutations
你可以在组件中使用 this.$store.commit('xxx')
提交 mutation,或者使用 mapMutations
辅助函数将组件中的 methods 映射为 store.commit
调用(需要在根节点注入 store
)。
如:
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')`
})
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Actions
Action 类似于 mutation,不同在于:
- Action 提交的是 mutation,而不是直接变更状态。
- Action 可以包含任意异步操作。
demo:
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
},
actions: {
increment (context) {
context.commit('increment')
}
}
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit
提交一个 mutation,或者通过 context.state
和 context.getters
来获取 state 和 getters。当我们在之后介绍到 Modules (opens new window) 时,你就知道 context 对象为什么不是 store 实例本身了。
实践中,我们会经常用到 ES2015 的 参数解构 (opens new window) (opens new window)来简化代码(特别是我们需要调用 commit
很多次的时候):
actions: {
increment ({ commit }) {
commit('increment')
}
}
2
3
4
5
# 触发Action
# 分类Action
Action 通过 store.dispatch
方法触发:
store.dispatch('increment')
// 以载荷形式分发
store.dispatch('incrementAsync', {
amount: 10
})
// 以对象形式分发
store.dispatch({
type: 'incrementAsync',
amount: 10
})
2
3
4
5
6
7
8
9
10
# mapActions
import { mapActions } from 'vuex'
export default {
// ...
methods: {
...mapActions([
'increment', // 将 `this.increment()` 映射为 `this.$store.dispatch('increment')`
// `mapActions` 也支持载荷:
'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.dispatch('incrementBy', amount)`
]),
...mapActions({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.dispatch('increment')`
})
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 组合action
如果我们利用 async / await (opens new window) (opens new window),我们可以如下组合 action:
// 假设 getData() 和 getOtherData() 返回的是 Promise
actions: {
async actionA ({ commit }) {
commit('gotData', await getData())
},
async actionB ({ dispatch, commit }) {
await dispatch('actionA') // 等待 actionA 完成
commit('gotOtherData', await getOtherData())
}
}
2
3
4
5
6
7
8
9
10
11