大家好,我是江城开朗的豌豆,一名拥有6年以上前端开发经验的工程师。我精通HTML、CSS、JavaScript等基础前端技术,并深入掌握Vue、React、Uniapp、Flutter等主流框架,能够高效解决各类前端开发问题。在我的技术栈中,除了常见的前端开发技术,我还擅长3D开发,熟练使用Three.js进行3D图形绘制,并在虚拟现实与数字孪生技术上积累了丰富的经验,特别是在虚幻引擎开发方面,有着深入的理解和实践。

我一直认为技术的不断探索和实践是进步的源泉,近年来,我深入研究大数据算法的应用与发展,尤其在数据可视化和交互体验方面,取得了显著的成果。我也注重与团队的合作,能够有效地推动项目的进展和优化开发流程。现在,我担任全栈工程师,拥有CSDN博客专家认证及阿里云专家博主称号,希望通过分享我的技术心得与经验,帮助更多人提升自己的技术水平,成为更优秀的开发者。
技术qq交流群:906392632
大家好,我是小杨,一个做了6年前端的老司机。今天咱们来聊聊Vue中组件传值那些事儿,这可是Vue开发中最基础也最常用的技能之一。掌握了这些技巧,你的组件之间就能愉快地"聊天"了!
为什么需要组件传值?
想象一下,我开发了一个电商网站,商品列表组件需要把用户选中的商品信息传递给购物车组件,这就是典型的组件通信场景。Vue提供了多种传值方式,咱们一个个来看。
1. 父传子:Props大法好
这是最常用的方式,就像爸爸给儿子零花钱一样简单。
            
            
              html
              
              
            
          
          <!-- 父组件 -->
<template>
  <div>
    <child-component :money="myMoney" />
  </div>
</template>
<script>
import ChildComponent from './ChildComponent.vue'
export default {
  components: { ChildComponent },
  data() {
    return {
      myMoney: 1000 // 这是我的私房钱
    }
  }
}
</script>
<!-- 子组件 -->
<template>
  <div>爸爸给了我 {{ money }} 块钱</div>
</template>
<script>
export default {
  props: {
    money: {
      type: Number,
      default: 0
    }
  }
}
</script>
        注意点:
- Props是单向数据流,子组件不能直接修改父组件传过来的值
 - 可以用
.sync修饰符实现"双向绑定"的效果(Vue 2.x) - Vue 3中推荐用
v-model替代.sync 
2. 子传父:自定义事件
儿子要钱花,得跟爸爸打个招呼不是?
            
            
              html
              
              
            
          
          <!-- 子组件 -->
<template>
  <button @click="askForMoney">爸,我没钱吃饭了</button>
</template>
<script>
export default {
  methods: {
    askForMoney() {
      this.$emit('give-me-money', 200) // 触发自定义事件
    }
  }
}
</script>
<!-- 父组件 -->
<template>
  <div>
    <child-component @give-me-money="handleGiveMoney" />
    <p>儿子要了 {{ givenMoney }} 块钱</p>
  </div>
</template>
<script>
import ChildComponent from './ChildComponent.vue'
export default {
  components: { ChildComponent },
  data() {
    return {
      givenMoney: 0
    }
  },
  methods: {
    handleGiveMoney(amount) {
      this.givenMoney = amount
      console.log('这小子又要钱!')
    }
  }
}
</script>
        3. 兄弟组件通信:Event Bus
两兄弟之间直接传话,不用经过老爸了!
            
            
              js
              
              
            
          
          // eventBus.js
import Vue from 'vue'
export const EventBus = new Vue()
// 兄弟组件A
<script>
import { EventBus } from './eventBus'
export default {
  methods: {
    tellBrother() {
      EventBus.$emit('message', '妈让你回家吃饭')
    }
  }
}
</script>
// 兄弟组件B
<script>
import { EventBus } from './eventBus'
export default {
  created() {
    EventBus.$on('message', (msg) => {
      console.log(`哥哥说:${msg}`)
    })
  }
}
</script>
        注意: Vue 3中因为移除了$on等方法,推荐使用mitt等第三方库替代Event Bus。
4. Vuex:全局状态管理
家里事儿太多太乱?上个家庭管理系统!
            
            
              js
              
              
            
          
          // store.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
  state: {
    familyMoney: 10000 // 家庭总资产
  },
  mutations: {
    spendMoney(state, amount) {
      state.familyMoney -= amount
    }
  },
  actions: {
    buySomething({ commit }, amount) {
      commit('spendMoney', amount)
    }
  }
})
// 任意组件中使用
<script>
export default {
  methods: {
    buyGame() {
      this.$store.dispatch('buySomething', 300)
      console.log(`我偷偷花了300块买游戏,家里还剩${this.$store.state.familyMoney}`)
    }
  }
}
</script>
        5. provide/inject:跨层级传值
爷爷直接给孙子传家宝,跳过爸爸这一辈!
            
            
              html
              
              
            
          
          <!-- 祖先组件 -->
<script>
export default {
  provide() {
    return {
      familyRelic: '祖传的玉佩'
    }
  }
}
</script>
<!-- 后代组件(任何层级都可以) -->
<script>
export default {
  inject: ['familyRelic'],
  created() {
    console.log(`我继承了${this.familyRelic}`) // 我继承了祖传的玉佩
  }
}
</script>
        6. $refs:直接访问组件实例
有时候直接"吼"一声比传纸条快
            
            
              html
              
              
            
          
          <template>
  <child-component ref="child" />
  <button @click="callChild">叫儿子</button>
</template>
<script>
export default {
  methods: {
    callChild() {
      this.$refs.child.sayHello()
      console.log('我直接调用了子组件的方法')
    }
  }
}
</script>
        7. <math xmlns="http://www.w3.org/1998/Math/MathML"> a t t r s / attrs/ </math>attrs/listeners:高阶传值技巧
不想一层层传递props?试试这个!
            
            
              html
              
              
            
          
          <!-- 父组件 -->
<child-component :name="myName" :age="30" @click="handleClick" />
<!-- 中间组件 -->
<grand-child v-bind="$attrs" v-on="$listeners" />
<!-- 最终子组件 -->
<script>
export default {
  created() {
    console.log(this.$attrs) // { name: '小杨', age: 30 }
  }
}
</script>
        小杨的实战建议
- 简单场景:用props和自定义事件就够了
 - 兄弟通信:小型项目用Event Bus,大型项目直接上Vuex
 - 跨层级:provide/inject很方便但别滥用
 - 全局状态:Vuex适合中大型项目,小项目可能杀鸡用牛刀
 - 直接访问:$refs很方便,但破坏了组件封装性,慎用
 
记住,没有最好的方式,只有最适合的方式。我在实际项目中经常混合使用这些方法,关键是要保持代码清晰可维护。
最后说两句
组件通信就像人与人之间的交流,方式很多,重要的是选择合适的场景。刚入行时我也经常为传值问题头疼,现在回头看,其实掌握了核心原理就很简单。希望这篇分享能帮你少走弯路!