Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它可以让我们更方便地管理应用程序的数据流,实现组件之间的通信,以及构建复杂的业务逻辑。在本文中,我将以 Vuex 的使用详解为主题,介绍 Vuex 的基本用法和高级用法,以及一些常见的问题和解决方案。本文假设你已经对 Vue.js 有一定的了解。
Unpkg.com 提供了基于 npm 的 CDN 链接。以上的链接会一直指向 npm 上发布的最新版本。您也可以通过 https://unpkg.com/vuex@4.0.0/dist/vuex.global.js 这样的方式指定特定的版本。
shellnpm install vuex@next --save
shellyarn add vuex@next --save
如果需要使用 dev 分支下的最新版本,您可以直接从 GitHub 上克隆代码并自己构建。
shellgit clone https://github.com/vuejs/vuex.git node_modules/vuex cd node_modules/vuex yarn yarn build
Vuex
的核心概念是state
、mutation
、action
和getter
。
state
是存储在 Vuex
中的数据,它是响应式的,也就是说当状态发生变化时,依赖于状态的组件也会自动更新。
mutation
是唯一能够改变状态的方法,它是同步的,并且每次突变都会被记录下来,方便调试和追踪。
action
是异步的操作,它可以包含任意的逻辑和副作用,并且通过提交突变来改变状态。
getter
是对状态的派生或计算属性,它可以让我们更方便地获取和处理状态。
要使用 Vuex
,我们首先需要创建一个存储器(store
)实例,并将其注册到根组件中:
js// 创建一个 store 实例
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment(state) {
state.count++
}
},
actions: {
incrementAsync({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
},
getters: {
doubleCount(state) {
return state.count * 2
}
}
})
new Vue({
el: '#app',
store,
// ...
})
注册了 store
之后,我们就可以在组件中通过 this.$store
访问 store
实例,并通过以下几种方式使用 Vuex
:
使用 mapState
辅助函数来将状态映射到组件的计算属性中:
jsimport { mapState } from 'vuex'
export default {
computed: {
...mapState({
count: state => state.count,
countAlias: 'count',
countPlusLocalState (state) {
return state.count + this.localCount
}
})
}
}
使用 mapMutations
辅助函数来将突变映射到组件的方法中:
jsimport { mapMutations } from 'vuex'
export default {
methods: {
...mapMutations([
'increment',
'incrementBy'
]),
...mapMutations({
add: 'increment'
})
}
}
使用 mapActions
辅助函数来将动作映射到组件的方法中:
jsimport { mapActions } from 'vuex'
export default {
methods: {
...mapActions([
'increment',
'incrementBy'
]),
...mapActions({
add: 'increment'
})
}
}
使用 mapGetters
辅助函数来将获取器映射到组件的计算属性中:
jsimport { mapGetters } from 'vuex'
export default {
computed: {
...mapGetters([
'doubleCount',
'anotherGetter'
])
}
}
Vuex
还提供了一些高级用法,让我们可以更灵活和高效地使用 Vuex
。这些高级用法包括:
module
):Vuex
允许我们将 store
分割成多个模块,每个模块拥有自己的state
、mutation
、action
和getter
。这样可以让我们更好地组织和管理复杂的状态。例如:js// 创建一个模块 a
const moduleA = {
state: () => ({
count: 0
}),
mutations: {
increment (state) {
state.count++
}
},
actions: {
incrementIfOdd ({ state, commit }) {
if (state.count % 2 === 1) {
commit('increment')
}
}
},
getters: {
doubleCount (state) {
return state.count * 2
}
}
}
// 创建一个模块 b
const moduleB = {
state: () => ({
name: 'Alice'
}),
mutations: {
changeName (state, newName) {
state.name = newName
}
},
actions: {
changeNameAsync ({ commit }, newName) {
setTimeout(() => {
commit('changeName', newName)
}, 1000)
}
},
getters: {
nameLength (state) {
return state.name.length
}
}
}
然后,我们可以在创建 store
实例时,通过 modules
选项来注册模块:
js// 创建一个 store 实例
const store = new Vuex.Store({
modules: {
a: moduleA,
b: moduleB
}
})
这样,我们就可以通过 store.state.a
和 store.state.b
来访问模块 a
和模块 b
的状态。
同样,我们也可以通过 store.commit('a/increment')
和 store.commit('b/changeName', 'Bob')
来提交模块 a
和模块 b
的mutation
。action
和getter
也是类似的,只需要在前面加上模块的命名空间即可。
如果我们想要让模块的动作和获取器不受命名空间的限制,我们可以在注册模块时,通过 namespaced: false
来关闭命名空间。
这样,我们就可以直接使用 store.dispatch('incrementIfOdd')
和 store.getters['doubleCount']
来调用模块 a
的action
和getter
。
但是,这样做可能会导致命名冲突的问题,所以建议使用命名空间来避免混淆。
Vuex
还提供了一些辅助函数来简化模块化的使用。例如:
createNamespacedHelpers
函数来创建一个带有命名空间的辅助函数集合:jsimport { createNamespacedHelpers } from 'vuex'
const { mapState, mapActions, mapGetters, mapMutations } = createNamespacedHelpers('some/nested/module')
export default {
computed: {
// 在 `some/nested/module` 中查找
...mapState({
a: state => state.a
}),
...mapGetters({
b: 'b'
})
},
methods: {
// 在 `some/nested/module` 中查找
...mapActions([
'foo'
]),
...mapMutations({
bar: 'bar'
})
}
}
createLogger
插件来打印每次突变的日志:jsimport createLogger from 'vuex/dist/logger'
const store = new Vuex.Store({
plugins: [createLogger()]
})
createPersistedState
插件来将状态持久化到本地存储中:jsimport createPersistedState from 'vuex-persistedstate'
const store = new Vuex.Store({
plugins: [createPersistedState()]
})
Vuex
的使用可能会遇到一些常见的问题和困难,这里列举了一些常见问题和解决方案,希望对你有所帮助。
有时候,我们可能需要在组件外部访问 store
,例如在路由钩子函数或者 API
请求中。这时候,我们可以通过导出 store
实例,并在需要的地方导入它来实现。例如:
js// store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export const store = new Vuex.Store({
// ...
})
// router.js
import { store } from './store'
router.beforeEach((to, from, next) => {
// 访问 store
if (store.state.user) {
next()
} else {
next('/login')
}
})
// api.js
import { store } from './store'
import axios from 'axios'
export function getUserInfo () {
// 访问 store
const token = store.state.token
return axios.get('/user/info', {
headers: {
Authorization: `Bearer ${token}`
}
})
}
Vuex
的mutation
是同步的,也就是说,我们不能在mutation
中执行异步操作,例如 setTimeout
、fetch
、axios
等。
如果我们需要执行异步操作,我们应该在action
中执行,并在异步操作完成后,提交mutation
来改变state
。例如:
js// 错误的做法:在突变中执行异步操作
mutations: {
incrementAsync (state) {
setTimeout(() => {
state.count++
}, 1000)
}
}
// 正确的做法:在动作中执行异步操作,并提交突变
actions: {
incrementAsync ({ commit }) {
setTimeout(() => {
commit('increment')
}, 1000)
}
}
有时候,我们可能需要将多个action
组合起来,形成一个复杂的业务逻辑。这时候,我们可以使用 Promise
或者 async/await
来处理异步流程。例如:
jsactions: {
// 使用 Promise
actionA ({ commit }) {
return new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
commit('someMutation')
resolve()
}, 1000)
})
},
// 使用 async/await
async actionB ({ dispatch, commit }) {
await dispatch('actionA') // 等待 actionA 完成
commit('anotherMutation')
}
}
Vuex
的状态是响应式的,也就是说,当state
发生变化时,依赖于state
的组件也会自动更新。
但是,这并不意味着我们可以直接在组件中修改state
,因为这样会破坏 Vuex
的状态管理模式。
如果我们想要实现表单的双向绑定,我们有以下几种方法:
v-model
的修饰符 .lazy
,并在 input
或 change
事件中提交mutation
:html<input v-model.lazy="message" @change="updateMessage">
jscomputed: {
message: {
get () {
return this.$store.state.message
},
set (value) {
this.updateMessage(value)
}
}
},
methods: {
updateMessage (value) {
this.$store.commit('updateMessage', value)
}
}
v-model
的修饰符 .number
或 .trim
,并在 input
或 change
事件中提交mutation
:html<input v-model.number="age" type="number" @input="updateAge">
<input v-model.trim="name" @input="updateName">
jscomputed: {
age: {
get () {
return this.$store.state.age
},
set (value) {
this.updateAge(value)
}
},
name: {
get () {
return this.$store.state.name
},
set (value) {
this.updateName(value)
}
}
},
methods: {
updateAge (value) {
this.$store.commit('updateAge', value)
},
updateName (value) {
this.$store.commit('updateName', value)
}
}
computed
属性的 get
和 set
方法,并在 set
方法中提交mutation
:html<input v-model="message">
jscomputed: {
message: {
get () {
return this.$store.state.message
},
set (value) {
this.$store.commit('updateMessage', value)
}
}
}
Vuex
是一个专为 Vue.js
应用程序开发的状态管理模式。它可以让我们更方便地管理应用程序的数据流,实现组件之间的通信,以及构建复杂的业务逻辑。
Vuex 的核心概念是state
、mutation
、action
、getter
和module
。Vuex
还提供了一些进阶用法,让我们可以更灵活和高效地使用 Vuex
。Vuex
的使用可能会遇到一些常见的问题和困难,文中列举了一些常见问题和解决方案。
本文作者:CreatorRay
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!