🚀 作者 :“码上有前”
🚀 文章简介 :前端高频面试题
🚀 欢迎小伙伴们 点赞👍、收藏⭐、留言💬
前端高频面试题--Vue组件通信篇
- 往期精彩内容
- Vue与VueComponent的关系
- props / $emit
- 父子组件传值
- 父子组件传值案例
- EventBus事件总线
- 依赖注入(provide / inject)
- ref / $refs
- $parent / $children
- $attrs / $listeners
- 总结
往期精彩内容
【前端高频面试题–HTML篇】
【前端高频面试题–CSS上篇】
【前端高频面试题–CSS下篇】
【前端高频面试题–JS上篇】
【前端高频面试题–JS下篇】
【前端高频面试题–ES6篇】
【前端高频面试题–ES7-ES11】
【前端–异步编程】
【前端高频面试题–TypeScript篇】
【前端高频面试题–git篇】
【前端高频面试题–微信小程序篇】
【前端高频面试题–Vue基础篇】
【前端高频面试题–虚拟DOM篇】
【前端高频面试题–Vue3.0篇】
【前端高频面试题–Vuex上篇】
【前端高频面试题–Vuex下篇】
【前端高频面试题–Vue生命周期篇】
【前端高频面试题–Vue组件通信篇】
【前端高频面试题–Vue路由篇】
【前端-Vue3创建一个新项目】
【前端大屏自适应缩放】
【前端Vue3 + TS项目开发一般流程】
Vue与VueComponent的关系
在Vue.js中,Vue是一个用于构建用户界面的JavaScript框架,而Vue组件(Vue Component)是构成Vue应用程序的基本构建块。
Vue组件是可复用的代码模块,它封装了一部分具有相关功能和样式的HTML、CSS和JavaScript代码。每个Vue组件都有自己的模板(template)、数据(data)、方法(methods)、计算属性(computed)、生命周期钩子(lifecycle hooks)等。
Vue组件可以通过Vue实例进行渲染和使用。在Vue应用程序中,可以创建多个Vue组件,然后将它们组合在一起形成一个完整的应用程序。
Vue实例是Vue的根实例,它是一个Vue组件的顶层容器。Vue实例通过指定要渲染的根组件来启动Vue应用程序。根组件可以包含其他子组件,并且它们可以通过组件之间的数据传递和事件通信来交互。
简而言之,Vue是整个框架,而Vue组件是构成Vue应用程序的可重用模块。Vue实例是Vue应用程序的根实例,用于启动和管理Vue组件。
props / $emit
props
和 $emit
是 Vue.js 中用于实现父子组件通信的两个关键概念。
Props:
Props 是一种从父组件向子组件传递数据的机制。父组件可以通过在子组件上定义属性(props),将数据传递给子组件。子组件接收到 props 后,可以在其模板中直接使用这些属性的值。
具体步骤如下:
- 在父组件中,通过在子组件上使用 v-bind 或简写的冒号语法,将数据绑定到子组件的属性上。例如:
<child-component :prop-name="dataValue"></child-component>
。 - 在子组件中,通过在组件选项中定义
props
属性来声明需要接收的属性。例如:props: ['propName']
。 - 子组件在接收到父组件传递的属性后,可以直接在模板中使用这些属性的值。例如:
<p>{{ propName }}</p>
。
Props 具有单向数据流的特性,父组件通过 props 向子组件传递数据,而子组件不能直接修改 props 的值。如果需要子组件修改父组件的数据,可以使用 $emit
。
**
e
m
i
t
∗
∗
:
‘
emit**: `
emit∗∗:‘emit是 Vue.js 实例提供的方法,用于触发自定义事件。通过
$emit`,子组件可以向父组件发送消息或数据,实现子组件向父组件通信。
具体步骤如下:
- 在子组件中,使用
this.$emit
方法触发一个自定义事件,并传递需要发送的数据。例如:this.$emit('custom-event', eventData)
。 - 在父组件中,通过在子组件上使用
v-on
或简写的@
符号语法,监听子组件触发的事件,并在相应的处理函数中处理接收到的数据。例如:<child-component @custom-event="handleEvent"></child-component>
。 - 在父组件中定义处理事件的方法
handleEvent
,该方法会在子组件触发事件时被调用,并接收到子组件传递的数据。
通过这种方式,子组件可以将需要与父组件共享的数据通过自定义事件的方式传递给父组件,实现了子组件向父组件通信的目的。
总结:
Props 是一种父组件向子组件传递数据的机制,而 $emit 是一种子组件向父组件发送消息或数据的机制。通过这两种机制,父子组件可以实现双向的通信,实现了组件之间的数据交互和共享。
父子组件传值
在 Vue.js 中,父组件向子组件传递值可以使用 props
,而子组件向父组件传递值可以使用 $emit
。
-
父组件向子组件传递值(使用 props):
- 在父组件中,通过在子组件上使用
v-bind
或简写的冒号语法,将数据绑定到子组件的属性上。例如:<child-component :prop-name="dataValue"></child-component>
。 - 在子组件中,通过在组件选项中定义
props
属性来声明需要接收的属性。例如:props: ['propName']
。 - 子组件可以直接在模板中使用通过 props 传递的值。例如:
<p>{{ propName }}</p>
。
- 在父组件中,通过在子组件上使用
-
子组件向父组件传递值(使用 $emit):
- 在子组件中,使用
this.$emit
方法触发一个自定义事件,并传递需要发送的数据。例如:this.$emit('custom-event', eventData)
。 - 在父组件中,通过在子组件上使用
v-on
或简写的@
符号语法,监听子组件触发的事件,并在相应的处理函数中处理接收到的数据。例如:<child-component @custom-event="handleEvent"></child-component>
。 - 在父组件中定义处理事件的方法
handleEvent
,该方法会在子组件触发事件时被调用,并接收到子组件传递的数据。
- 在子组件中,使用
这样,就可以在父子组件之间实现数据的传递和通信。父组件通过 props 将数据传递给子组件,子组件接收并使用这些值。而子组件通过 $emit
触发自定义事件,并将需要传递的数据发送给父组件,父组件监听并处理这些事件,获取子组件传递的值。
需要注意的是,在父子组件之间传递数据时,数据的类型和命名需要保持一致,以确保正确的数据传递和使用。
父子组件传值案例
当涉及到父子组件之间的数据传递时,一个常见的案例是一个待办事项列表(Todo List)。假设我们有一个父组件 TodoList
和一个子组件 TodoItem
,我们希望通过父组件向子组件传递每个待办事项的数据。
以下是一个简单的父子组件传值的案例:
<!-- 父组件 TodoList -->
<template>
<div>
<h2>Todo List</h2>
<ul>
<li v-for="todo in todos" :key="todo.id">
<!-- 通过 props 传递数据给子组件 -->
<todo-item :todo="todo"></todo-item>
</li>
</ul>
</div>
</template>
<script>
import TodoItem from './TodoItem.vue';
export default {
components: {
TodoItem
},
data() {
return {
todos: [
{ id: 1, text: 'Buy groceries' },
{ id: 2, text: 'Clean the house' },
{ id: 3, text: 'Go for a run' }
]
};
}
};
</script>
<!-- 子组件 TodoItem -->
<template>
<div>
<!-- 使用通过 props 接收的数据 -->
<span>{{ todo.text }}</span>
<button @click="completeTodo">Complete</button>
</div>
</template>
<script>
export default {
props: {
// 声明父组件传递的属性
todo: {
type: Object,
required: true
}
},
methods: {
completeTodo() {
// 子组件向父组件传递数据
this.$emit('todo-completed', this.todo.id);
}
}
};
</script>
在上述案例中,父组件 TodoList
中的 todos
数组包含了待办事项的数据。通过使用 v-for
遍历数组,在每个 TodoItem
子组件上使用 prop
todo
将每个待办事项的对象传递给子组件。
在子组件 TodoItem
中,通过定义 props
并接收 todo
属性,可以在子组件中访问父组件传递的待办事项对象。子组件还定义了 completeTodo
方法,当用户点击 “Complete” 按钮时,子组件触发了一个自定义事件 todo-completed
并传递了待办事项的 id
值,以便父组件进行相应的处理。
父组件监听 TodoItem
子组件触发的 todo-completed
事件,并在相应的处理函数中获取子组件传递的 id
值,从而实现父子组件之间的数据传递和通信。
EventBus事件总线
EventBus(事件总线)是一种在 Vue.js 中实现组件间通信的机制。它可以用于跨组件传递事件和数据,包括父子组件、兄弟组件或任意组件之间的通信。
EventBus 是一个 Vue 实例,它充当中央事件处理器,用于将事件发送到一个组件,然后由其他组件监听和接收这些事件。
以下是使用 EventBus 实现组件间通信的基本步骤:
-
创建 EventBus 实例:
// event-bus.js import Vue from 'vue'; export const eventBus = new Vue();
-
在发送组件中发送事件:
// SenderComponent.vue import { eventBus } from './event-bus.js'; export default { methods: { sendMessage() { eventBus.$emit('custom-event', eventData); } } }
-
在接收组件中监听事件:
// ReceiverComponent.vue import { eventBus } from './event-bus.js'; export default { created() { eventBus.$on('custom-event', this.handleEvent); }, methods: { handleEvent(eventData) { // 处理接收到的事件及数据 } } }
在上述例子中,我们首先在 event-bus.js
文件中创建了一个新的 Vue 实例 eventBus
。这个实例可以作为事件总线,用来处理组件间的事件通信。
然后,在发送组件 SenderComponent
中,我们使用 eventBus.$emit
方法发送了一个名为 'custom-event'
的自定义事件,并传递了相应的数据 eventData
。
在接收组件 ReceiverComponent
中,我们使用 eventBus.$on
方法监听了 'custom-event'
事件,并在 created
钩子函数中注册了事件处理函数 handleEvent
。当事件被触发时,该函数将被调用,并传递事件数据 eventData
。
通过这种方式,我们可以实现在不同组件之间的事件和数据传递。EventBus 提供了一种简单而强大的方式,让组件可以松散地耦合并进行通信,从而提高了组件的可重用性和灵活性。然而,需要注意使用 EventBus 时要避免滥用,合理使用它来满足组件间通信的需求。
依赖注入(provide / inject)
在 Vue.js 中,依赖注入是一种用于在组件树中共享数据或功能的高级技术。Vue 提供了 provide
和 inject
选项来实现依赖注入。
使用依赖注入,父组件可以通过 provide
选项提供数据或方法,而子组件可以通过 inject
选项注入这些数据或方法。这样,父组件提供的数据或方法可以在整个子组件树中访问和使用,而不需要显式地通过 props 一层一层传递。
以下是使用 provide
和 inject
实现依赖注入的示例:
<!-- 父组件 ParentComponent -->
<template>
<div>
<child-component></child-component>
</div>
</template>
<script>
import { provide } from 'vue';
import MyDependency from './MyDependency';
export default {
components: {
ChildComponent
},
setup() {
provide('dependency', MyDependency);
}
};
</script>
<!-- 子组件 ChildComponent -->
<template>
<div>
<p>{{ dependencyData }}</p>
<button @click="callDependencyMethod">Call Method</button>
</div>
</template>
<script>
import { inject } from 'vue';
export default {
setup() {
const dependency = inject('dependency');
const dependencyData = dependency.getData();
const callDependencyMethod = () => {
dependency.someMethod();
};
return {
dependencyData,
callDependencyMethod
};
}
};
</script>
在上述示例中,父组件 ParentComponent
使用 provide
选项提供了一个名为 'dependency'
的依赖项,其值为 MyDependency
对象。
子组件 ChildComponent
使用 inject
选项来注入父组件提供的 'dependency'
依赖项。通过注入,子组件可以访问到 MyDependency
对象,并使用其提供的数据和方法。
在子组件中,我们通过调用 dependency.getData()
方法获取依赖项的数据,并通过 dependency.someMethod()
调用依赖项的方法。子组件可以直接在模板中使用这些数据和方法。
通过依赖注入,我们可以将共享的数据或方法提供给整个组件树中的组件,而无需在每个组件之间显式传递。这种模式可以大大简化组件之间的通信,并提高代码的可维护性和灵活性。
ref / $refs
在 Vue.js 中,ref
是一个用于给组件或DOM元素注册引用的特殊属性,而$refs
是一个用于在组件中访问这些引用的对象。
使用 ref
,我们可以在组件或DOM元素上定义一个引用名称,然后通过$refs
对象来访问该引用。
以下是一个示例:
<template>
<div>
<input type="text" ref="myInput" />
<button @click="focusInput">Focus Input</button>
</div>
</template>
<script>
export default {
methods: {
focusInput() {
this.$refs.myInput.focus();
}
}
};
</script>
在上述示例中,我们在<input>
元素上使用ref
属性来定义一个引用名称为myInput
。
在focusInput
方法中,我们使用this.$refs.myInput
来访问引用。通过this.$refs.myInput
,我们可以像访问DOM元素一样,调用其属性和方法。在这里,我们调用了focus()
方法来聚焦输入框。
需要注意的是,$refs
是一个对象,其中的键是我们在ref
属性中定义的引用名称,值是对应的组件实例或DOM元素。
需要注意的是,$refs
是在组件渲染完成后才会填充的,所以在组件的mounted
钩子函数或之后的生命周期钩子中使用$refs
才是可靠的。
另外,需要注意的是,$refs
只在直接子组件或DOM元素上有效。如果要访问子组件中的引用,可以通过$refs
继续深入访问,例如this.$refs.childComponent.$refs.myInput
。
总结来说,ref
用于在组件或DOM元素上定义引用,$refs
用于在组件中访问这些引用,从而方便地操作和访问组件或DOM元素的属性和方法。
$parent / $children
在 Vue.js 中,$parent
和 $children
是用于访问组件实例的特殊属性。
$parent
属性用于访问当前组件的父组件实例。$children
属性用于访问当前组件的直接子组件实例的数组。
以下是一个示例:
<template>
<div>
<h1>{{ message }}</h1>
<child-component></child-component>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
data() {
return {
message: 'Hello, Parent!'
};
},
mounted() {
console.log(this.$parent); // 访问父组件实例
console.log(this.$children); // 访问直接子组件实例的数组
}
};
</script>
在上述示例中,我们有一个父组件和一个子组件。在父组件中,我们可以通过 this.$parent
访问当前组件的父组件实例。在子组件中,我们可以通过 this.$parent
访问子组件的父组件实例。
同样,我们可以通过 this.$children
来访问当前组件的直接子组件实例的数组。注意,$children
是一个数组,包含了当前组件的直接子组件实例。
需要注意的是,使用 $parent
和 $children
可能会导致组件之间的强耦合,因为它们暴露了组件的层次结构。在大型应用程序中,最好避免过度依赖于这些属性,而是使用 props 和事件来实现组件之间的通信。
此外,由于在 Vue 3 中,$parent
和 $children
不再是响应式的,因此在 Vue 3 中,更推荐使用 provide / inject 或 Vuex 等状态管理库来处理跨组件的通信需求。
$attrs / $listeners
在 Vue.js 中,$attrs
和 $listeners
是用于访问父组件传递给子组件的属性和监听器的特殊属性。
$attrs
属性包含了父组件传递给子组件的非 prop 属性。它是一个对象,包含了父组件传递的所有属性(除了子组件声明的 props 外)。$listeners
属性包含了父组件传递给子组件的所有监听器。它是一个对象,包含了父组件传递的所有事件监听器。
以下是一个示例:
<!-- 父组件 ParentComponent -->
<template>
<div>
<child-component
message="Hello, Child!"
color="blue"
@custom-event="handleEvent"
></child-component>
</div>
</template>
<script>
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
methods: {
handleEvent() {
// 处理事件
}
}
};
</script>
<!-- 子组件 ChildComponent -->
<template>
<div>
<h1>{{ message }}</h1>
<button :style="{ color }" @click="$emit('custom-event')">Click Me</button>
</div>
</template>
<script>
export default {
props: ['message'], // 声明 prop
mounted() {
console.log(this.$attrs); // 访问父组件传递的非 prop 属性
console.log(this.$listeners); // 访问父组件传递的监听器
},
data() {
return {
color: this.$attrs.color // 访问父组件传递的非 prop 属性
};
}
};
</script>
在上述示例中,父组件通过 message
和 color
属性以及 custom-event
事件监听器将数据和事件传递给子组件。
在子组件中,我们可以通过 this.$attrs
来访问父组件传递的非 prop 属性。在示例中,我们可以通过 this.$attrs.color
来获取 color
属性的值。
通过 this.$listeners
,我们可以访问父组件传递的所有监听器。在示例中,我们绑定了 @custom-event
事件监听器,所以可以在子组件中通过 $emit('custom-event')
触发该事件。
需要注意的是,$attrs
和 $listeners
是仅在子组件内部可用的,它们不会自动传递给更深层次的子组件。如果需要将这些特性传递给下一级子组件,可以使用 v-bind="$attrs"
和 v-on="$listeners"
。
总结来说,$attrs
用于访问父组件传递给子组件的非 prop 属性,$listeners
用于访问父组件传递给子组件的监听器。这些特性可以使子组件更灵活地处理父组件传递的属性和事件。
总结
在 Vue.js 中,有几种常见的方式可以实现不同组件之间的通信,包括父子组件间通信、兄弟组件间通信以及任意组件间通信。下面是对每种通信方式的总结:
-
父子组件间通信:
- Props:父组件通过 props 将数据传递给子组件。子组件通过定义和使用 props 属性来接收父组件传递的数据。
- $emit 和事件:子组件通过 $emit 方法触发自定义事件,并通过父组件中的 v-on 指令监听和处理这些事件。
- r e f s :父组件可以通过 r e f 属性引用子组件,并通过 t h i s . refs:父组件可以通过 ref 属性引用子组件,并通过 this. refs:父组件可以通过ref属性引用子组件,并通过this.refs 来访问子组件的实例,从而直接调用子组件的方法或访问子组件的属性。
-
兄弟组件间通信:
- 共享状态:可以使用一个共享的状态管理库(如 Vuex)或通过一个共同的父组件来管理和共享数据。
- 事件总线:创建一个空的 Vue 实例作为事件总线,兄弟组件通过该实例进行事件的发布和订阅,从而实现兄弟组件之间的通信。
- 父组件作为中间人:通过将共享数据放在共同的父组件中,然后通过 props 和自定义事件在兄弟组件之间传递数据。
-
任意组件间通信:
- 事件总线:创建一个空的 Vue 实例作为事件总线,任意组件都可以通过该实例进行事件的发布和订阅,实现组件之间的通信。
- provide / inject:父组件通过 provide 来提供数据,然后子孙组件通过 inject 来注入这些数据,实现跨层级的组件通信。
- 全局事件总线:创建一个全局的事件总线对象,任意组件都可以通过该对象进行事件的发布和订阅。
需要根据具体的场景和需求选择适当的通信方式。对于简单的组件通信,props 和自定义事件通常足够;对于较复杂的通信需求,可以考虑使用状态管理库或事件总线来管理和共享数据。