Vuex实战:从懵逼到真香的心路历程

131 阅读2分钟

大家好,我是小杨,一个在Vue世界里摸爬滚打6年的老司机。今天要和大家聊聊Vuex这个"甜蜜的负担"——刚入门时觉得它像丈母娘的要求又多又烦,用顺手后发现简直是开发大型项目的救命稻草!

(温馨提示:本文附带咖啡店点单系统完整代码示例,文末自取~)


一、🌰 举个栗子:没有Vuex的日子有多苦?

去年我接了个咖啡店点餐系统的项目,刚开始想着:"不就几个页面传数据嘛,用props和event足够了!"

结果代码写成这样:

// 父组件
<ChildA :order="order" @update="handleUpdate"/>
<ChildB :order="order" @update="handleUpdate"/>
<ChildC :order="order"/> 

// 子组件
this.$emit('update', newOrder)

三天后:

  • 页面层级深到需要props穿越5层组件
  • 兄弟组件通信要靠共同的祖爷爷组件中转
  • 改个需求要翻遍十几个文件

我意识到——是时候请出Vuex这个救兵了!


二、🚀 Vuex四件套:咖啡店的中央管理系统

想象Vuex就是咖啡店的中央控制台

1. State - 咖啡店库存表

state: {
  coffeeBeans: 100,  // 咖啡豆库存
  milk: 50,          // 牛奶库存
  currentOrder: null // 当前订单
}

2. Mutations - 唯一能改库存的人(店长)

mutations: {
  SET_ORDER(state, payload) {
    state.currentOrder = payload
  },
  USE_INGREDIENTS(state, {beans, milk}) {
    state.coffeeBeans -= beans
    state.milk -= milk
  }
}

3. Actions - 咖啡师的工作流程

actions: {
  async makeCoffee({ commit, state }, order) {
    if (state.coffeeBeans < 10) {
      我.alert('咖啡豆不足!')
      return
    }
    
    await 我.煮咖啡() // 模拟异步操作
    commit('USE_INGREDIENTS', { beans: 10, milk: 5 })
    commit('SET_ORDER', order)
  }
}

4. Getters - 今日特饮推荐

getters: {
  recommend: state => {
    return state.coffeeBeans > 50 
      ? '推荐手冲咖啡' 
      : '今日特价:美式咖啡'
  }
}

三、💻 在项目中如何食用?

1. 安装并注入Store

// store/index.js
export default new Vuex.Store({
  state,
  mutations,
  actions,
  getters
})

// main.js
import store from './store'
new Vue({ store }).$mount('#app')

2. 组件中使用(三种姿势)

姿势一:直接调用(适合紧急情况)

this.$store.dispatch('makeCoffee', '拿铁')
const recommend = this.$store.getters.recommend

姿势二:map辅助函数(优雅永不过时)

import { mapActions, mapGetters } from 'vuex'

export default {
  methods: {
    ...mapActions(['makeCoffee'])
  },
  computed: {
    ...mapGetters(['recommend'])
  }
}

姿势三:Composition API(Vue3真香)

import { useStore } from 'vuex'

setup() {
  const store = useStore()
  const recommend = computed(() => store.getters.recommend)
  
  const orderCoffee = () => {
    store.dispatch('makeCoffee', '澳白')
  }
}

四、🚨 血泪教训:我踩过的那些坑

  1. Mutation必须同步!
    有次我在mutation里调接口,导致devtools无法追踪状态变化...

  2. Module的命名空间
    忘记加namespaced: true,导致action重名冲突:

    // 错误示范
    this.$store.dispatch('updateOrder') // 调用的是哪个模块的?
    
    // 正确姿势
    this.$store.dispatch('order/updateOrder')
    
  3. Store太大怎么办?
    模块化拆分,就像咖啡店分前厅和后厨:

    modules: {
      order: orderModule,  // 订单相关
      menu: menuModule,    // 菜单管理
      user: userModule     // 用户信息
    }
    

五、🌟 小杨的私藏最佳实践

  1. 目录结构这样排

    store/
    ├── index.js        # 组装模块
    ├── actions.js      # 根级action
    ├── modules/
    │   ├── cart.js     # 购物车模块
    │   └── user.js     # 用户模块
    
  2. 配合LocalStorage持久化

    // 用户登录后保存状态
    localStorage.setItem('user', JSON.stringify(state.user))
    
    // 初始化时读取
    state.user = JSON.parse(localStorage.getItem('user')) || {}
    
  3. Vuex+TypeScript的正确姿势

    interface CoffeeState {
      beans: number
      milk: number
    }
    
    const state: CoffeeState = {
      beans: 100,
      milk: 50
    }
    

⭐  写在最后

请大家不吝赐教,在下方评论或者私信我,十分感谢🙏🙏🙏.

✅ 认为我某个部分的设计过于繁琐,有更加简单或者更高逼格的封装方式

✅ 认为我部分代码过于老旧,可以提供新的API或最新语法

✅ 对于文章中部分内容不理解

✅ 解答我文章中一些疑问

✅ 认为某些交互,功能需要优化,发现BUG

✅ 想要添加新功能,对于整体的设计,外观有更好的建议

✅ 一起探讨技术加qq交流群:906392632

最后感谢各位的耐心观看,既然都到这了,点个 👍赞再走吧!