
当我们去面试前端岗位的时候,难免会问到数据传递的问题,今天小编来分享几个Vue组件面试题:8种方法解决父子/兄弟/跨级组件数据传递。
为什么组件通信是vue的核心痛点?
真实场景还原:
某电商项目因组件通信混乱,导致:
数据触目惊心:
8种通信方案终极对比(附场景+代码)
<!-- 父组件:传递数据和接收事件 --> <template> <ChildComponent :PRoduct="currentProduct" @add-to-cart="handleAddToCart" /> </template> <!-- 子组件:触发事件 --> <script> Export default { methods: { addToCart() { this.$emit('add-to-cart', { id: this.product.id, quantity: this.selectedQty }) } } } </script>
<!-- 父组件 --> <Child v-model:title="pagetitle" /> <!-- 子组件 --> <script> export default { props: ['title'], emits: ['update:title'], methods: { changeTitle() { this.$emit('upDate:title', '新标题') } } } </script>
方案2:Event Bus(全局事件总线)
实现步骤:
创建全局事件中心
// EventBus.JS import { ref } from 'Vue' const events = ref(new map()) export default { $on(event, callback) { if (!events.value.has(event)) { events.value.set(event, []) } events.value.get(event).push(callback) }, $off(event, callback) { const listeners = events.value.get(event) if (listeners) { events.value.set( event, listeners.filter(l => l !== callback) ) } }, $emit(event, ...args) { const listeners = events.value.get(event) listeners?.forEach(callback => callback(...args)) } }
组件中使用
<!-- 组件A:发送事件 -->
<script>
import eventBus from './eventBus'
export default {
methods: {
notifyCartUpdate() {
eventBus.$emit('cart-updated', { count: 5 })
}
}
}
</script>
<!-- 组件B:监听事件 -->
<script>
import eventBus from './eventBus'
export default {
created() {
eventBus.$on('cart-updated', this.handleCartUpdate)
},
beforeUnmount() {
eventBus.$off('cart-updated', this.handleCartUpdate)
},
methods: {
handleCartUpdate(Payload) {
console.log('购物车数量更新:', payload.count)
}
}
}
</script>适用场景:非父子组件解耦通信(如跨页面通知)
致命陷阱:
忘记在beforeUnmount中移除监听 → 内存泄漏!
事件命名冲突 → 使用命名空间(如user:login)
方案3:Provide / inject(跨层级传递)
祖孙组件传参最佳实践:
<!-- 祖先组件 -->
<script>
import { provide } from 'vue'
export default {
setup() {
// 提供响应式数据
const theme = ref('dark')
provide('theme', theme)
// 提供修改方法
const toggletheme = () => {
theme.value = theme.value === 'dark' ? 'light' : 'dark'
}
provide('toggleTheme', toggleTheme)
}
}
</script>
<!-- 后代组件 -->
<script>
import { inject } from 'vue'
export default {
Setup() {
const theme = inject('theme')
const toggleTheme = inject('toggleTheme')
return { theme, toggleTheme }
}
}
</script>避免滥用!优先考虑props传递
为inject提供默认值防止未定义:
const theme = inject('theme', () => 'light', true)
8种方案决策树(一键匹配最佳实践)
graph TD
A[需要共享状态吗?]
A -->|是| B{状态是否频繁变化?}
A -->|否| C[使用Props/Event]
B -->|是| D[Pinia/vuex]
B -->|否| E[provide/inject]
C --> F{组件层级关系?}
F -->|父子| G[Props + emit]
F -->|兄弟| H[Event Bus]
F -->|祖孙| I[Provide/Inject]
D --> J{需要持久化吗?}
J -->|是| K[Vuex + 插件]
J -->|否| L[Pinia]三大高频错误现场还原
错误1:直接修改props引发的血案
<!-- 错误写法 -->
<script>
export default {
props: ['user'],
methods: {
changeName() {
this.user.name = '张三' // 控制台警告!
}
}
}
</script>
<!-- 正确方案 -->
<script>
export default {
props: ['user'],
emits: ['update:user'],
methods: {
changeName() {
const newUser = { ...this.user, name: '张三' }
this.$emit('update:user', newUser)
}
}
}
</script>错误2:事件总线内存泄漏
// 错误:未及时移除监听 mounted() { eventBus.$on('update', this.handleUpdate) } // 正确:组件销毁时清理 beforeUnmount() { eventBus.$off('update', this.handleUpdate) }
错误3:Provide响应式丢失
// 错误:直接传递非响应式对象 provide('config', { theme: 'dark' }) // 正确:使用ref/reactive包裹 const config = reactive({ theme: 'dark' }) provide('config', config)








网友评论文明上网理性发言 已有0人参与
发表评论: