DEV Community

Tianya School
Tianya School

Posted on

Deep Dive into Vue.js Component Communication Methods

Vue.js component communication is achieved through several methods, each suited to specific use cases. Below is a comprehensive overview of these methods, including code examples and their characteristics.

Props

  • Direction: Parent to Child
  • Purpose: Allows a parent component to pass data to a child component via attributes.
  • Characteristics:
    • Read-Only: Props are immutable by default in the child component.
    • Validation: Props can have defined validation rules.
    • Dynamic: Props can update with changes in the parent component’s state.

Parent Component (Parent.vue)

<template>
  <div>
    <ChildComponent :message="parentMessage" />
  </div>
</template>

<script>
import ChildComponent from './ChildComponent.vue';

export default {
  components: {
    ChildComponent
  },
  data() {
    return {
      parentMessage: 'Hello from Parent'
    };
  }
};
</script>
Enter fullscreen mode Exit fullscreen mode

Child Component (ChildComponent.vue)

<template>
  <div>
    <p>{{ message }}</p>
  </div>
</template>

<script>
export default {
  props: {
    message: String
  }
};
</script>
Enter fullscreen mode Exit fullscreen mode

$emit (Events)

  • Direction: Child to Parent
  • Purpose: Enables a child component to notify the parent by triggering custom events.
  • Characteristics:
    • Data Transmission: Events can carry data payloads.
    • Multiple Events: A child can emit multiple distinct events.

Child Component (ChildComponent.vue)

<template>
  <button @click="notifyParent">Notify Parent</button>
</template>

<script>
export default {
  methods: {
    notifyParent() {
      this.$emit('child-event', 'Hello from Child');
    }
  }
};
</script>
Enter fullscreen mode Exit fullscreen mode

Parent Component (Parent.vue)

<template>
  <ChildComponent @child-event="handleChildEvent" />
</template>

<script>
import ChildComponent from './ChildComponent.vue';

export default {
  components: {
    ChildComponent
  },
  methods: {
    handleChildEvent(data) {
      console.log('Received data from child:', data);
    }
  }
};
</script>
Enter fullscreen mode Exit fullscreen mode

Vuex

  • Global State Management
  • Purpose: Manages application-wide state accessible by any component.
  • Characteristics:
    • State: Stores global state data.
    • Mutations: Synchronous operations, the only way to modify state.
    • Actions: Handle asynchronous operations, dispatching mutations.
    • Getters: Computed properties based on state, cached for efficiency.
    • Modules: Split state management into modules for large applications.

Setting Up Vuex Store (store.js)

import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

export default new Vuex.Store({
  state: {
    sharedCounter: 0
  },
  mutations: {
    increment(state) {
      state.sharedCounter++;
    }
  },
  actions: {
    incrementAsync({ commit }) {
      setTimeout(() => {
        commit('increment');
      }, 1000);
    }
  },
  getters: {
    getCounter: state => state.sharedCounter
  }
});
Enter fullscreen mode Exit fullscreen mode

Main Application (main.js)

Ensure the application integrates the Vuex Store:

import Vue from 'vue';
import App from './App.vue';
import store from './store';

new Vue({
  store,
  render: h => h(App)
}).$mount('#app');
Enter fullscreen mode Exit fullscreen mode

Using Vuex in Components (ComponentUsingVuex.vue)

<template>
  <div>
    <p>Counter: {{ counter }}</p>
    <button @click="increment">Increment</button>
    <button @click="incrementAsync">Increment Async</button>
  </div>
</template>

<script>
export default {
  computed: {
    counter() {
      return this.$store.getters.getCounter;
    }
  },
  methods: {
    increment() {
      this.$store.commit('increment');
    },
    incrementAsync() {
      this.$store.dispatch('incrementAsync');
    }
  }
};
</script>
Enter fullscreen mode Exit fullscreen mode

Provide/Inject

  • Cross-Level Communication
  • Purpose: Allows an ancestor component to provide data that descendants can inject, bypassing intermediate parent-child hierarchies.
  • Characteristics:
    • Hierarchy-Independent: Works across multiple levels but may increase code coupling.
    • Limited Use: Best suited for library or framework-level data passing, not general component communication.

Ancestor Component (AncestorComponent.vue)

<template>
  <div>
    <ChildComponent />
  </div>
</template>

<script>
export default {
  provide() {
    return {
      ancestorValue: 'Value from Ancestor'
    };
  }
};
</script>
Enter fullscreen mode Exit fullscreen mode

Descendant Component (DescendantComponent.vue)

<template>
  <div>
    <p>Value from Ancestor: {{ ancestorValue }}</p>
  </div>
</template>

<script>
export default {
  inject: ['ancestorValue'],
  mounted() {
    console.log('Injected value:', this.ancestorValue);
  }
};
</script>
Enter fullscreen mode Exit fullscreen mode

Ref and v-model

  • Direct Reference
  • Purpose: Allows a parent to directly access a child component instance or enable two-way data binding.
  • Characteristics:
    • Refs: Used to access child component instances or DOM elements for direct manipulation.
    • v-model: Facilitates two-way data binding, commonly used with form elements or custom components.

Parent Component (ParentComponent.vue)

<template>
  <ChildComponent ref="childRef" v-model="parentValue" />
  <button @click="logChildRef">Log Child Ref</button>
</template>

<script>
import ChildComponent from './ChildComponent.vue';

export default {
  components: {
    ChildComponent
  },
  data() {
    return {
      parentValue: 'Initial Value'
    };
  },
  methods: {
    logChildRef() {
      console.log(this.$refs.childRef);
    }
  }
};
</script>
Enter fullscreen mode Exit fullscreen mode

Child Component (ChildComponent.vue)

<template>
  <input :value="value" @input="$emit('input', $event.target.value)" />
</template>

<script>
export default {
  props: ['value']
};
</script>
Enter fullscreen mode Exit fullscreen mode

Custom Events

  • Event-Based Communication
  • Purpose: Enables non-standard communication between components via custom events.
  • Characteristics:
    • Flexible: Can be triggered and listened to between any components.
    • Complex Interactions: Suitable for specific interactions or intricate component communication.

Child Component (ChildComponent.vue)

<template>
  <button @click="customEvent">Send Custom Event</button>
</template>

<script>
export default {
  methods: {
    customEvent() {
      this.$emit('custom-event', 'Data to send');
    }
  }
};
</script>
Enter fullscreen mode Exit fullscreen mode

Parent Component (ParentComponent.vue)

<template>
  <ChildComponent @custom-event="handleCustomEvent" />
</template>

<script>
import ChildComponent from './ChildComponent.vue';

export default {
  components: {
    ChildComponent
  },
  methods: {
    handleCustomEvent(data) {
      console.log('Received custom event data:', data);
    }
  }
};
</script>
Enter fullscreen mode Exit fullscreen mode

Slots

  • Content Distribution
  • Purpose: Allows a parent component to insert content into specific areas of a child component.
  • Characteristics:
    • Default Slot: The default content area in a child component.
    • Named Slots: Multiple slots can be defined, with the parent specifying content placement.
    • Scoped Slots: The parent can access child component data to customize slot content.

Parent Component (ParentComponent.vue)

<template>
  <WrapperComponent>
    <h1 slot="header">Custom Header</h1>
    <p slot="body">Custom Body Content</p>
  </WrapperComponent>
</template>

<script>
import WrapperComponent from './WrapperComponent.vue';

export default {
  components: {
    WrapperComponent
  }
};
</script>
Enter fullscreen mode Exit fullscreen mode

Wrapper Component (WrapperComponent.vue)

<template>
  <div>
    <slot name="header"></slot>
    <div class="content">
      <slot name="body"></slot>
    </div>
  </div>
</template>
Enter fullscreen mode Exit fullscreen mode

Composition API

  • New Feature
  • Purpose: Introduced in Vue 3, it provides better logic and data organization within components.
  • Characteristics:
    • setup() Function: Runs at the start of the component lifecycle, accessing props and lifecycle hooks.
    • ref and reactive: Manage reactive data.
    • provide and inject: Enhanced implementations for flexible cross-component data passing.

Parent Component (ParentComponent.vue)

<template>
  <ChildComponent :count="count" @updateCount="updateCount" />
</template>

<script>
import { ref, onMounted } from 'vue';
import ChildComponent from './ChildComponent.vue';

export default {
  components: {
    ChildComponent
  },
  setup() {
    const count = ref(0);

    function updateCount(newCount) {
      count.value = newCount;
    }

    onMounted(() => {
      console.log('Initial count:', count.value);
    });

    return {
      count,
      updateCount
    };
  }
};
</script>
Enter fullscreen mode Exit fullscreen mode

Child Component (ChildComponent.vue)

<template>
  <button @click="increment">Increment</button>
</template>

<script>
import { ref } from 'vue';

export default {
  props: ['count'],
  setup(props, { emit }) {
    const count = ref(props.count);

    function increment() {
      count.value++;
      emit('updateCount', count.value);
    }

    return {
      count,
      increment
    };
  }
};
</script>
Enter fullscreen mode Exit fullscreen mode

👉 Click to join and systematically improve development capabilities: Advanced Development Tutorial

Top comments (0)