第一个Vuex Store #
目标 #
创建一个完整的计数器 Store,包含以下功能:
- 基本计数功能
- 异步增加功能
- 派生状态(双倍计数)
创建 Store #
1. 定义状态 #
javascript
// src/store/index.js
import { createStore } from 'vuex'
export default createStore({
// 定义状态
state: {
count: 0,
user: null,
loading: false
}
})
2. 定义变更 #
javascript
import { createStore } from 'vuex'
export default createStore({
state: {
count: 0,
user: null,
loading: false
},
// 定义变更
mutations: {
// 增加计数
INCREMENT(state) {
state.count++
},
// 减少计数
DECREMENT(state) {
state.count--
},
// 设置计数
SET_COUNT(state, value) {
state.count = value
},
// 设置用户
SET_USER(state, user) {
state.user = user
},
// 设置加载状态
SET_LOADING(state, loading) {
state.loading = loading
}
}
})
3. 定义动作 #
javascript
import { createStore } from 'vuex'
export default createStore({
state: {
count: 0,
user: null,
loading: false
},
mutations: {
INCREMENT(state) {
state.count++
},
DECREMENT(state) {
state.count--
},
SET_COUNT(state, value) {
state.count = value
},
SET_USER(state, user) {
state.user = user
},
SET_LOADING(state, loading) {
state.loading = loading
}
},
// 定义动作
actions: {
// 同步增加
increment({ commit }) {
commit('INCREMENT')
},
// 同步减少
decrement({ commit }) {
commit('DECREMENT')
},
// 异步增加
async incrementAsync({ commit }) {
commit('SET_LOADING', true)
// 模拟异步操作
await new Promise(resolve => setTimeout(resolve, 1000))
commit('INCREMENT')
commit('SET_LOADING', false)
},
// 获取用户信息
async fetchUser({ commit }) {
commit('SET_LOADING', true)
try {
const response = await fetch('/api/user')
const user = await response.json()
commit('SET_USER', user)
} catch (error) {
console.error('获取用户失败:', error)
} finally {
commit('SET_LOADING', false)
}
}
}
})
4. 定义派生状态 #
javascript
import { createStore } from 'vuex'
export default createStore({
state: {
count: 0,
user: null,
loading: false
},
mutations: {
INCREMENT(state) {
state.count++
},
DECREMENT(state) {
state.count--
},
SET_COUNT(state, value) {
state.count = value
},
SET_USER(state, user) {
state.user = user
},
SET_LOADING(state, loading) {
state.loading = loading
}
},
actions: {
increment({ commit }) {
commit('INCREMENT')
},
decrement({ commit }) {
commit('DECREMENT')
},
async incrementAsync({ commit }) {
commit('SET_LOADING', true)
await new Promise(resolve => setTimeout(resolve, 1000))
commit('INCREMENT')
commit('SET_LOADING', false)
},
async fetchUser({ commit }) {
commit('SET_LOADING', true)
try {
const response = await fetch('/api/user')
const user = await response.json()
commit('SET_USER', user)
} catch (error) {
console.error('获取用户失败:', error)
} finally {
commit('SET_LOADING', false)
}
}
},
// 定义派生状态
getters: {
// 双倍计数
doubleCount: state => state.count * 2,
// 计数是否为正数
isPositive: state => state.count > 0,
// 计数描述
countDescription: state => {
if (state.count === 0) return '零'
if (state.count > 0) return `正数: ${state.count}`
return `负数: ${state.count}`
},
// 用户是否登录
isLoggedIn: state => state.user !== null,
// 用户名
userName: state => state.user?.name || '游客'
}
})
在 Vue 中注册 #
javascript
// src/main.js
import { createApp } from 'vue'
import App from './App.vue'
import store from './store'
const app = createApp(App)
app.use(store)
app.mount('#app')
在组件中使用 #
方式一:直接访问 #
vue
<template>
<div>
<p>Count: {{ $store.state.count }}</p>
<p>Double: {{ $store.getters.doubleCount }}</p>
<button @click="$store.commit('INCREMENT')">+1</button>
<button @click="$store.dispatch('incrementAsync')">
Async +1
</button>
</div>
</template>
方式二:使用辅助函数(推荐) #
vue
<template>
<div>
<p>Count: {{ count }}</p>
<p>Double: {{ doubleCount }}</p>
<p>Status: {{ countDescription }}</p>
<p>User: {{ userName }}</p>
<div v-if="loading">Loading...</div>
<button @click="increment">+1</button>
<button @click="decrement">-1</button>
<button @click="incrementAsync" :disabled="loading">
Async +1
</button>
</div>
</template>
<script>
import { mapState, mapGetters, mapActions } from 'vuex'
export default {
computed: {
// 映射状态
...mapState(['count', 'loading']),
// 映射派生状态
...mapGetters([
'doubleCount',
'countDescription',
'userName'
])
},
methods: {
// 映射动作
...mapActions([
'increment',
'decrement',
'incrementAsync'
])
}
}
</script>
方式三:组合式 API(Vue 3) #
vue
<template>
<div>
<p>Count: {{ count }}</p>
<p>Double: {{ doubleCount }}</p>
<button @click="increment">+1</button>
</div>
</template>
<script>
import { computed } from 'vue'
import { useStore } from 'vuex'
export default {
setup() {
const store = useStore()
return {
count: computed(() => store.state.count),
doubleCount: computed(() => store.getters.doubleCount),
increment: () => store.commit('INCREMENT')
}
}
}
</script>
完整示例 #
Store 完整代码 #
javascript
// src/store/index.js
import { createStore } from 'vuex'
export default createStore({
state: {
count: 0,
user: null,
loading: false,
error: null
},
mutations: {
INCREMENT(state) {
state.count++
},
DECREMENT(state) {
state.count--
},
SET_COUNT(state, value) {
state.count = value
},
SET_USER(state, user) {
state.user = user
},
SET_LOADING(state, loading) {
state.loading = loading
},
SET_ERROR(state, error) {
state.error = error
},
CLEAR_ERROR(state) {
state.error = null
}
},
actions: {
increment({ commit }) {
commit('INCREMENT')
},
decrement({ commit }) {
commit('DECREMENT')
},
async incrementAsync({ commit }) {
commit('SET_LOADING', true)
commit('CLEAR_ERROR')
try {
await new Promise(resolve => setTimeout(resolve, 1000))
commit('INCREMENT')
} catch (error) {
commit('SET_ERROR', error.message)
} finally {
commit('SET_LOADING', false)
}
},
async fetchUser({ commit }) {
commit('SET_LOADING', true)
commit('CLEAR_ERROR')
try {
const response = await fetch('/api/user')
if (!response.ok) throw new Error('获取用户失败')
const user = await response.json()
commit('SET_USER', user)
} catch (error) {
commit('SET_ERROR', error.message)
} finally {
commit('SET_LOADING', false)
}
},
async login({ commit }, credentials) {
commit('SET_LOADING', true)
commit('CLEAR_ERROR')
try {
const response = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(credentials)
})
if (!response.ok) throw new Error('登录失败')
const user = await response.json()
commit('SET_USER', user)
} catch (error) {
commit('SET_ERROR', error.message)
} finally {
commit('SET_LOADING', false)
}
},
logout({ commit }) {
commit('SET_USER', null)
}
},
getters: {
doubleCount: state => state.count * 2,
isPositive: state => state.count > 0,
countDescription: state => {
if (state.count === 0) return '零'
if (state.count > 0) return `正数: ${state.count}`
return `负数: ${state.count}`
},
isLoggedIn: state => state.user !== null,
userName: state => state.user?.name || '游客',
hasError: state => state.error !== null
}
})
组件完整代码 #
vue
<!-- src/components/Counter.vue -->
<template>
<div class="counter">
<h2>计数器示例</h2>
<div class="display">
<p class="count">Count: {{ count }}</p>
<p class="double">Double: {{ doubleCount }}</p>
<p class="status">{{ countDescription }}</p>
</div>
<div v-if="loading" class="loading">
Loading...
</div>
<div v-if="hasError" class="error">
{{ error }}
</div>
<div class="buttons">
<button @click="decrement" :disabled="loading">-1</button>
<button @click="increment" :disabled="loading">+1</button>
<button @click="incrementAsync" :disabled="loading">
Async +1
</button>
</div>
</div>
</template>
<script>
import { mapState, mapGetters, mapActions } from 'vuex'
export default {
name: 'Counter',
computed: {
...mapState(['count', 'loading', 'error']),
...mapGetters([
'doubleCount',
'countDescription',
'hasError'
])
},
methods: {
...mapActions([
'increment',
'decrement',
'incrementAsync'
])
}
}
</script>
<style scoped>
.counter {
max-width: 400px;
margin: 0 auto;
padding: 20px;
text-align: center;
}
.display {
margin: 20px 0;
}
.count {
font-size: 24px;
font-weight: bold;
}
.buttons {
display: flex;
gap: 10px;
justify-content: center;
}
button {
padding: 10px 20px;
font-size: 16px;
cursor: pointer;
}
button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.loading {
color: blue;
margin: 10px 0;
}
.error {
color: red;
margin: 10px 0;
}
</style>
调试技巧 #
使用 Vue DevTools #
- 安装 Vue DevTools 浏览器扩展
- 打开开发者工具,切换到 Vue 标签
- 选择 Vuex 选项卡
功能说明 #
text
Vue DevTools Vuex 功能
├── State ──────── 查看当前状态
├── Getters ────── 查看派生状态
├── Mutations ──── 查看变更历史
├── Time Travel ── 回到任意历史状态
└── Export ─────── 导出状态快照
总结 #
创建一个 Vuex Store 的步骤:
- 定义 State - 存储应用数据
- 定义 Mutations - 同步修改状态
- 定义 Actions - 处理异步操作
- 定义 Getters - 计算派生状态
- 注册 Store - 在 Vue 应用中注册
- 使用 Store - 在组件中访问和修改
继续学习 State状态,深入了解状态管理。
最后更新:2026-03-28