Vuex状态管理 #

一、Vuex简介 #

Vuex是Vue.js的状态管理模式,采用集中式存储管理应用的所有组件的状态。

1.1 安装 #

bash
npm install vuex@next

1.2 基本配置 #

javascript
// store/index.js
import { createStore } from 'vuex'

export default createStore({
  state: {
    count: 0
  },
  mutations: {
    increment(state) {
      state.count++
    }
  },
  actions: {
    increment({ commit }) {
      commit('increment')
    }
  },
  getters: {
    doubledCount(state) {
      return state.count * 2
    }
  }
})
javascript
// 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')

二、核心概念 #

2.1 State #

javascript
// store/index.js
export default createStore({
  state: {
    count: 0,
    user: {
      name: '张三',
      age: 25
    },
    todos: [
      { id: 1, text: '学习Vue', completed: false },
      { id: 2, text: '学习Vuex', completed: true }
    ]
  }
})
vue
<template>
  <div>
    <p>计数: {{ $store.state.count }}</p>
    <p>用户: {{ $store.state.user.name }}</p>
  </div>
</template>

<script setup>
import { useStore } from 'vuex'
import { computed } from 'vue'

const store = useStore()

// 访问state
console.log(store.state.count)

// 使用computed保持响应性
const count = computed(() => store.state.count)
</script>

2.2 mapState辅助函数 #

vue
<script setup>
import { useStore, mapState } from 'vuex'
import { computed } from 'vue'

const store = useStore()

// 使用mapState
const { count, user } = mapState(['count', 'user'])

// 或展开使用
const count = computed(() => store.state.count)
const userName = computed(() => store.state.user.name)
</script>

三、Getters #

3.1 定义Getters #

javascript
export default createStore({
  state: {
    todos: [
      { id: 1, text: '学习Vue', completed: false },
      { id: 2, text: '学习Vuex', completed: true }
    ]
  },
  
  getters: {
    // 基本getter
    doneTodos: (state) => {
      return state.todos.filter(todo => todo.completed)
    },
    
    // 返回函数
    getTodoById: (state) => (id) => {
      return state.todos.find(todo => todo.id === id)
    },
    
    // 访问其他getter
    doneTodosCount: (state, getters) => {
      return getters.doneTodos.length
    }
  }
})

3.2 使用Getters #

vue
<template>
  <div>
    <p>已完成: {{ $store.getters.doneTodosCount }}</p>
  </div>
</template>

<script setup>
import { useStore } from 'vuex'
import { computed } from 'vue'

const store = useStore()

// 访问getter
const doneTodos = computed(() => store.getters.doneTodos)
const doneTodosCount = computed(() => store.getters.doneTodosCount)

// 使用返回函数的getter
const todo = store.getters.getTodoById(1)
</script>

四、Mutations #

4.1 定义Mutations #

javascript
export default createStore({
  state: {
    count: 0,
    user: null
  },
  
  mutations: {
    increment(state) {
      state.count++
    },
    
    incrementBy(state, payload) {
      state.count += payload.amount
    },
    
    setUser(state, user) {
      state.user = user
    },
    
    // 对象风格的提交
    updateUser(state, { field, value }) {
      state.user[field] = value
    }
  }
})

4.2 提交Mutations #

vue
<script setup>
import { useStore } from 'vuex'

const store = useStore()

// 基本提交
store.commit('increment')

// 载荷提交
store.commit('incrementBy', { amount: 10 })

// 对象风格提交
store.commit({
  type: 'incrementBy',
  amount: 10
})
</script>

4.3 Mutation规则 #

javascript
// ✅ 正确:同步操作
mutations: {
  increment(state) {
    state.count++
  }
}

// ❌ 错误:异步操作
mutations: {
  increment(state) {
    setTimeout(() => {
      state.count++  // 不要这样做!
    }, 1000)
  }
}

五、Actions #

5.1 定义Actions #

javascript
export default createStore({
  state: {
    products: [],
    loading: false
  },
  
  mutations: {
    setProducts(state, products) {
      state.products = products
    },
    setLoading(state, loading) {
      state.loading = loading
    }
  },
  
  actions: {
    async fetchProducts({ commit }) {
      commit('setLoading', true)
      try {
        const response = await fetch('/api/products')
        const products = await response.json()
        commit('setProducts', products)
      } finally {
        commit('setLoading', false)
      }
    },
    
    async addProduct({ commit }, product) {
      const response = await fetch('/api/products', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(product)
      })
      const newProduct = await response.json()
      commit('addProduct', newProduct)
    },
    
    // 组合action
    async fetchAndProcess({ dispatch, commit }) {
      await dispatch('fetchProducts')
      // 处理数据
      commit('processProducts')
    }
  }
})

5.2 分发Actions #

vue
<script setup>
import { useStore } from 'vue'
import { onMounted } from 'vue'

const store = useStore()

// 分发action
store.dispatch('fetchProducts')

// 带载荷
store.dispatch('addProduct', { name: '新产品', price: 99 })

// 对象风格
store.dispatch({
  type: 'addProduct',
  product: { name: '新产品', price: 99 }
})

// 处理Promise
onMounted(async () => {
  await store.dispatch('fetchProducts')
  console.log('数据加载完成')
})
</script>

六、Modules #

6.1 定义模块 #

javascript
// store/modules/user.js
export default {
  namespaced: true,
  
  state: {
    user: null,
    token: null
  },
  
  mutations: {
    setUser(state, user) {
      state.user = user
    },
    setToken(state, token) {
      state.token = token
    }
  },
  
  actions: {
    async login({ commit }, credentials) {
      const response = await fetch('/api/login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(credentials)
      })
      const data = await response.json()
      commit('setUser', data.user)
      commit('setToken', data.token)
    },
    
    logout({ commit }) {
      commit('setUser', null)
      commit('setToken', null)
    }
  },
  
  getters: {
    isLoggedIn: state => !!state.token,
    userName: state => state.user?.name
  }
}

6.2 注册模块 #

javascript
// store/index.js
import { createStore } from 'vuex'
import user from './modules/user'
import product from './modules/product'

export default createStore({
  modules: {
    user,
    product
  }
})

6.3 使用模块 #

vue
<script setup>
import { useStore } from 'vuex'
import { computed } from 'vue'

const store = useStore()

// 访问模块state
const user = computed(() => store.state.user.user)
const token = computed(() => store.state.user.token)

// 访问模块getter
const isLoggedIn = computed(() => store.getters['user/isLoggedIn'])

// 提交模块mutation
store.commit('user/setUser', { name: '张三' })

// 分发模块action
store.dispatch('user/login', { username: 'admin', password: '123456' })
</script>

七、完整示例 #

javascript
// store/index.js
import { createStore } from 'vuex'

export default createStore({
  state: {
    cart: [],
    products: []
  },
  
  mutations: {
    ADD_TO_CART(state, product) {
      const item = state.cart.find(i => i.id === product.id)
      if (item) {
        item.quantity++
      } else {
        state.cart.push({ ...product, quantity: 1 })
      }
    },
    
    REMOVE_FROM_CART(state, productId) {
      state.cart = state.cart.filter(i => i.id !== productId)
    },
    
    UPDATE_QUANTITY(state, { productId, quantity }) {
      const item = state.cart.find(i => i.id === productId)
      if (item) {
        item.quantity = quantity
      }
    },
    
    SET_PRODUCTS(state, products) {
      state.products = products
    }
  },
  
  actions: {
    async fetchProducts({ commit }) {
      const response = await fetch('/api/products')
      const products = await response.json()
      commit('SET_PRODUCTS', products)
    },
    
    addToCart({ commit, state }, product) {
      commit('ADD_TO_CART', product)
      // 可以在这里保存到本地存储
      localStorage.setItem('cart', JSON.stringify(state.cart))
    }
  },
  
  getters: {
    cartTotal: state => {
      return state.cart.reduce((total, item) => {
        return total + item.price * item.quantity
      }, 0)
    },
    
    cartItemCount: state => {
      return state.cart.reduce((count, item) => count + item.quantity, 0)
    },
    
    productById: state => id => {
      return state.products.find(p => p.id === id)
    }
  }
})
vue
<template>
  <div>
    <h2>购物车</h2>
    <p>商品数量: {{ cartItemCount }}</p>
    <p>总价: ¥{{ cartTotal }}</p>
    
    <div v-for="item in cart" :key="item.id">
      {{ item.name }} - ¥{{ item.price }} x {{ item.quantity }}
      <button @click="removeFromCart(item.id)">删除</button>
    </div>
  </div>
</template>

<script setup>
import { useStore } from 'vuex'
import { computed } from 'vue'

const store = useStore()

const cart = computed(() => store.state.cart)
const cartTotal = computed(() => store.getters.cartTotal)
const cartItemCount = computed(() => store.getters.cartItemCount)

function removeFromCart(productId) {
  store.commit('REMOVE_FROM_CART', productId)
}
</script>

八、总结 #

Vuex核心概念 #

概念 说明
State 状态数据
Getters 计算属性
Mutations 同步修改状态
Actions 异步操作

Vuex要点:

  • State存储应用状态
  • Getters派生状态
  • Mutations同步修改状态
  • Actions处理异步操作
  • Modules模块化管理
  • 使用命名空间避免冲突
最后更新:2026-03-26