Appearance
状态管理
为什么需要状态管理
在复杂的Vue.js应用中,组件之间的状态管理变得复杂。当多个组件需要共享和修改相同的状态时,组件间通信变得困难,数据流变得难以追踪。状态管理提供了一个中央存储来管理所有组件的状态。
简单的状态管理
对于简单的情况,可以使用响应式数据和provide/inject:
vue
<!-- store.js -->
import { reactive, readonly } from 'vue'
// 创建共享状态
const state = reactive({
count: 0,
user: null,
todos: []
})
// 创建修改状态的方法
const mutations = {
increment() {
state.count++
},
decrement() {
state.count--
},
setUser(user) {
state.user = user
},
addTodo(todo) {
state.todos.push({ id: Date.now(), ...todo })
},
removeTodo(id) {
state.todos = state.todos.filter(todo => todo.id !== id)
}
}
// 导出状态和方法
export const store = {
state: readonly(state), // 只读状态
...mutations
}
vue
<!-- 在组件中使用 -->
<script setup>
import { store } from './store'
function incrementCount() {
store.increment()
}
function addNewTodo() {
store.addTodo({ text: 'New todo', completed: false })
}
</script>
<template>
<div>
<p>Count: {{ store.state.count }}</p>
<p>User: {{ store.state.user?.name || 'Not logged in' }}</p>
<button @click="incrementCount">Increment</button>
<ul>
<li v-for="todo in store.state.todos" :key="todo.id">
{{ todo.text }}
</li>
</ul>
</div>
</template>
</template>
Vuex 状态管理
Vuex是Vue.js的官方状态管理库,适用于大型应用。
Vuex 基础结构
javascript
// store/index.js
import { createStore } from 'vuex'
export default createStore({
// 状态
state() {
return {
count: 0,
user: null,
todos: []
}
},
// 计算属性
getters: {
doubleCount: state => state.count * 2,
completedTodos: state => state.todos.filter(todo => todo.completed),
userHasTodos: (state, getters) => {
return state.user && getters.todosCount > 0
},
todosCount: state => state.todos.length
},
// 同步修改状态的方法
mutations: {
increment(state) {
state.count++
},
decrement(state) {
state.count--
},
setUser(state, user) {
state.user = user
},
setTodos(state, todos) {
state.todos = todos
},
addTodo(state, todo) {
state.todos.push({
id: Date.now(),
...todo,
completed: false
})
},
toggleTodo(state, id) {
const todo = state.todos.find(todo => todo.id === id)
if (todo) {
todo.completed = !todo.completed
}
},
removeTodo(state, id) {
const index = state.todos.findIndex(todo => todo.id === id)
if (index !== -1) {
state.todos.splice(index, 1)
}
}
},
// 异步操作
actions: {
async fetchUser({ commit }, userId) {
try {
const response = await fetch(`/api/users/${userId}`)
const user = await response.json()
commit('setUser', user)
} catch (error) {
console.error('Failed to fetch user:', error)
}
},
async fetchTodos({ commit }) {
try {
const response = await fetch('/api/todos')
const todos = await response.json()
commit('setTodos', todos)
} catch (error) {
console.error('Failed to fetch todos:', error)
}
},
async addTodo({ commit }, todoText) {
try {
const response = await fetch('/api/todos', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ text: todoText })
})
const todo = await response.json()
commit('addTodo', todo)
} catch (error) {
console.error('Failed to add todo:', error)
}
}
}
})
在组件中使用Vuex
vue
<script setup>
import { computed } from 'vue'
import { useStore } from 'vuex'
const store = useStore()
// 使用计算属性访问状态
const count = computed(() => store.state.count)
const doubleCount = computed(() => store.getters.doubleCount)
const todos = computed(() => store.state.todos)
// 方法
function increment() {
store.commit('increment')
}
function addTodo(text) {
store.dispatch('addTodo', text)
}
function toggleTodo(id) {
store.commit('toggleTodo', id)
}
</script>
<template>
<div>
<h2>Count: {{ count }} (Double: {{ doubleCount }})</h2>
<button @click="increment">Increment</button>
<div class="todos">
<h3>Todos</h3>
<ul>
<li v-for="todo in todos" :key="todo.id">
<input
type="checkbox"
:checked="todo.completed"
@change="toggleTodo(todo.id)"
>
<span :class="{ completed: todo.completed }">{{ todo.text }}</span>
</li>
</ul>
</div>
</div>
</template>
<style>
.completed {
text-decoration: line-through;
color: #999;
}
</style>
</template>