【前端高频面试题--Vue组件通信篇】

 作者 :“码上有前”
 文章简介 :前端高频面试题
 欢迎小伙伴们 点赞、收藏⭐、留言

前端高频面试题--Vue组件通信篇

  • props / $emit
  • 父子组件传值
  • 父子组件传值案例
  • EventBus事件总线
  • 依赖注入(provide / inject)
  • ref / $refs
  • $parent / $children
  • $attrs / $listeners
  • 总结

props / $emit

props$emit 是 Vue.js 中用于实现父子组件通信的两个关键概念。

Props
Props 是一种从父组件向子组件传递数据的机制。父组件可以通过在子组件上定义属性(props),将数据传递给子组件。子组件接收到 props 后,可以在其模板中直接使用这些属性的值。

具体步骤如下:

  1. 在父组件中,通过在子组件上使用 v-bind 或简写的冒号语法,将数据绑定到子组件的属性上。例如:
  2. 在子组件中,通过在组件选项中定义 props 属性来声明需要接收的属性。例如:props: ['propName']
  3. 子组件在接收到父组件传递的属性后,可以直接在模板中使用这些属性的值。例如:

    {{ propName }}

Props 具有单向数据流的特性,父组件通过 props 向子组件传递数据,而子组件不能直接修改 props 的值。如果需要子组件修改父组件的数据,可以使用 $emit

** e m i t ∗ ∗ : ‘ emit**: ` emitemit是 Vue.js 实例提供的方法,用于触发自定义事件。通过$emit`,子组件可以向父组件发送消息或数据,实现子组件向父组件通信。

具体步骤如下:

  1. 在子组件中,使用 this.$emit 方法触发一个自定义事件,并传递需要发送的数据。例如:this.$emit('custom-event', eventData)
  2. 在父组件中,通过在子组件上使用 v-on 或简写的@ 符号语法,监听子组件触发的事件,并在相应的处理函数中处理接收到的数据。例如:
  3. 在父组件中定义处理事件的方法 handleEvent,该方法会在子组件触发事件时被调用,并接收到子组件传递的数据。

通过这种方式,子组件可以将需要与父组件共享的数据通过自定义事件的方式传递给父组件,实现了子组件向父组件通信的目的。

总结:
Props 是一种父组件向子组件传递数据的机制,而 $emit 是一种子组件向父组件发送消息或数据的机制。通过这两种机制,父子组件可以实现双向的通信,实现了组件之间的数据交互和共享。

父子组件传值

在 Vue.js 中,父组件向子组件传递值可以使用 props,而子组件向父组件传递值可以使用 $emit

  1. 父组件向子组件传递值(使用 props)

    • 在父组件中,通过在子组件上使用 v-bind 或简写的冒号语法,将数据绑定到子组件的属性上。例如:
    • 在子组件中,通过在组件选项中定义 props 属性来声明需要接收的属性。例如:props: ['propName']
    • 子组件可以直接在模板中使用通过 props 传递的值。例如:

      {{ propName }}

  2. 子组件向父组件传递值(使用 $emit)

    • 在子组件中,使用 this.$emit 方法触发一个自定义事件,并传递需要发送的数据。例如:this.$emit('custom-event', eventData)
    • 在父组件中,通过在子组件上使用 v-on 或简写的 @ 符号语法,监听子组件触发的事件,并在相应的处理函数中处理接收到的数据。例如:
    • 在父组件中定义处理事件的方法 handleEvent,该方法会在子组件触发事件时被调用,并接收到子组件传递的数据。

这样,就可以在父子组件之间实现数据的传递和通信。父组件通过 props 将数据传递给子组件,子组件接收并使用这些值。而子组件通过 $emit 触发自定义事件,并将需要传递的数据发送给父组件,父组件监听并处理这些事件,获取子组件传递的值。

需要注意的是,在父子组件之间传递数据时,数据的类型和命名需要保持一致,以确保正确的数据传递和使用。

父子组件传值案例

当涉及到父子组件之间的数据传递时,一个常见的案例是一个待办事项列表(Todo List)。假设我们有一个父组件 TodoList 和一个子组件 TodoItem,我们希望通过父组件向子组件传递每个待办事项的数据。

以下是一个简单的父子组件传值的案例:


<template>
  <div>
    <h2>Todo Listh2>
    <ul>
      <li v-for="todo in todos" :key="todo.id">
        
        <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>

<template>
  <div>
    
    <span>{{ todo.text }}span>
    <button @click="completeTodo">Completebutton>
  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 实现组件间通信的基本步骤:

  1. 创建 EventBus 实例:

    // event-bus.js
    import Vue from 'vue';
    export const eventBus = new Vue();
    
  2. 在发送组件中发送事件:

    // SenderComponent.vue
    import { eventBus } from './event-bus.js';
    export default {
      methods: {
        sendMessage() {
          eventBus.$emit('custom-event', eventData);
        }
      }
    }
    
  3. 在接收组件中监听事件:

    // 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 提供了 provideinject 选项来实现依赖注入。

使用依赖注入,父组件可以通过 provide 选项提供数据或方法,而子组件可以通过 inject 选项注入这些数据或方法。这样,父组件提供的数据或方法可以在整个子组件树中访问和使用,而不需要显式地通过 props 一层一层传递。

以下是使用 provideinject 实现依赖注入的示例:


<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>

<template>
  <div>
    <p>{{ dependencyData }}p>
    <button @click="callDependencyMethod">Call Methodbutton>
  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 Inputbutton>
  div>
template>

<script>
export default {
  methods: {
    focusInput() {
      this.$refs.myInput.focus();
    }
  }
};
script>

在上述示例中,我们在元素上使用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 属性包含了父组件传递给子组件的所有监听器。它是一个对象,包含了父组件传递的所有事件监听器。

以下是一个示例:


<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>

<template>
  <div>
    <h1>{{ message }}h1>
    <button :style="{ color }" @click="$emit('custom-event')">Click Mebutton>
  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>

在上述示例中,父组件通过 messagecolor 属性以及 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 中,有几种常见的方式可以实现不同组件之间的通信,包括父子组件间通信、兄弟组件间通信以及任意组件间通信。下面是对每种通信方式的总结:

  1. 父子组件间通信

    • 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 来访问子组件的实例,从而直接调用子组件的方法或访问子组件的属性。
  2. 兄弟组件间通信

    • 共享状态:可以使用一个共享的状态管理库(如 Vuex)或通过一个共同的父组件来管理和共享数据。
    • 事件总线:创建一个空的 Vue 实例作为事件总线,兄弟组件通过该实例进行事件的发布和订阅,从而实现兄弟组件之间的通信。
    • 父组件作为中间人:通过将共享数据放在共同的父组件中,然后通过 props 和自定义事件在兄弟组件之间传递数据。
  3. 任意组件间通信

    • 事件总线:创建一个空的 Vue 实例作为事件总线,任意组件都可以通过该实例进行事件的发布和订阅,实现组件之间的通信。
    • provide / inject:父组件通过 provide 来提供数据,然后子孙组件通过 inject 来注入这些数据,实现跨层级的组件通信。
    • 全局事件总线:创建一个全局的事件总线对象,任意组件都可以通过该对象进行事件的发布和订阅。

需要根据具体的场景和需求选择适当的通信方式。对于简单的组件通信,props 和自定义事件通常足够;对于较复杂的通信需求,可以考虑使用状态管理库或事件总线来管理和共享数据。

你可能感兴趣的:(前端,前端,vue.js,javascript)