One of the most powerful features of Vue is the ability to create custom components that can be easily reused across different parts of your application. Whether you’re a seasoned Vue.js developer or just getting started with the framework, mastering Vue components can help you take your applications to the next level. In this comprehensive guide, we’ll explore everything you need to know about Vue components, from the basics of creating and using components to more advanced techniques like component communication and custom events. By the end of this guide, you’ll have a solid understanding of how to create powerful, reusable components that can help you build robust and scalable Vue applications. So let’s get started!
Defining a Vue Component
When using a build step to compile your application, it’s common to define your component as a Single-File Component (SFC for short) using the dedicated .vue file extension. There are other ways to define a component, but for this guide, we’ll be using the SFC syntax. The concepts are the same and you can find examples of other scenarios here.
Let’s start by looking at an example of a component, with a filename of ButtonCounter.vue
<script setup> import { ref } from 'vue' const count = ref(0) </script> <template> <button @click="count++">You clicked me {{ count }} times.</button> </template>
TIn the example above:
- The <script> tag will contain most of the Javascript/Typescript logic of your component.
- The <template> tag will contain the HTML of your component
Components can use other components by importing and using them within the <template> tag. If we were to use the ButtonCounter above in another component it might look like this:
<script setup> import ButtonCounter from './ButtonCounter.vue' </script> <template> <ButtonCounter /> // this is a child component </template>
It’s also recommended to use PascalCase instead of kebab-case to avoid naming conflicts with native HTML elements.
Vue Component Props
Vue component props allow you to pass data from a parent component to a child component. Props make your components more reusable and enable communication between components. Here’s an example of using component props:
import ChildComponent from './ChildComponent.vue'; <template> <div> <child-component :message="parentMessage"></child-component> </div> </template>
In the example above, we’re able to pass data into our child-component through the :message prop. Our child-component could display the message it receives from the parent component making it reusable throughout the application.
The example above also uses a dynamic property by using the “:” before the prop name. parentMessage could be a string, object, or array depending on how you define the component prop.
Same-name shorthand props
If your prop has the same name as the template value you can shorten this
<img :src="src">
To this
<img :src>
Define Props
To define the components props, you can use the `defineProps` method. If you’re using Typescript the example might look something like this:
<script setup lang="ts"> defineProps<{ message?: string }>() </script>
Vue Component Prop Data Flow
Data flow in Vue component props is one-way. Meaning the child component cannot mutate the value passed by the parent component. Anytime the parent component is updated, all props in the child component will be refreshed with the latest value. However, a child component can mutate the prop value when an Object or Array is passed as the value. In Javascript, these are passed by reference and the nested properties from the object or array can be modified by the child and affect the parent components state. Unless the parent and child components are tightly coupled by design, you should avoid this type of scenario which could make application state changes difficult to understand. Instead, it may be helpful to think of the parent component state as a single source of truth. If a child component needs to modify the parent’s state it should notify it through some other mechanism rather than directly modifying it. For example, the child component could emit an event and notify the parent to make such a change.
Prop Runtime Validation
Props can apply runtime validation beyond the static type checking provided by Typescript. the defineProps method accepts multiple configurations to describe the validation to be run during runtime. This could be something simple like checking if a value is a String or it could contain custom Javascript logic.
defineProps({ name: String department: { validator(value, props) { // The value must match one of these strings return ['sales', 'marketing', 'operations'].includes(value) } }, })
The astute Typescript user might notice that we’re type-checking name with String and not string. That’s because once the application is running, Typescript is non-existent and Vue uses the native constructors for runtime checking.
Vue Component Events
If component props allow a parent-to-child data flow, component events allow a child-to-parent data flow. Events can contain a specific value as an argument, or multiple arguments that will be forward in order. For example, when a child component has captured some data input and is ready to notify its parent, the value can be emitted using the `$emit` method:
<!-- MyComponent --> <button @click="$emit('someEvent', 1, true)">click me</button> <--Parent listens to the event from MyComponent--> <MyComponent @some-event="increaseCount" /> function increaseCount(n, isActive) { count.value += n; active.value = isActive; }
Similar to props, Emitted Events can declare runtime validation in the defineEmits method
<script setup> const emit = defineEmits({ // No validation click: null, // Validate submit event submit: ({ email, password }) => { if (email && password) { return true } else { console.warn('Invalid submit event payload!') return false } } }) function submitForm(email, password) { emit('submit', { email, password }) } </script>
The combination of props and events enables the use of presentational components. Props allow components to receive data, and events let child components notify parent components. This enables parent components to contain stateful logic and child components to be purely presentational without any knowledge of how, when, and from where data is received.
Vue Dynamic Component
Vue allows you to switch between different components dynamically based on conditions or user interactions. This flexibility enables you to create more interactive and adaptable UIs. Here’s an example of using dynamic components:
<template> <div> <component :is="currentComponent"></component> <button @click="toggleComponent">Toggle Component</button> </div> </template> <script> import FirstComponent from './FirstComponent.vue'; import SecondComponent from './SecondComponent.vue'; export default { data() { return { currentComponent: 'FirstComponent', }; }, methods: { toggleComponent() { this.currentComponent = this.currentComponent === 'FirstComponent' ? 'SecondComponent' : 'FirstComponent'; }, }, components: { FirstComponent, SecondComponent, }, } </script>
In the example above, we’re utilizing the special is attribute which accepts either the name string of the registered component OR the actual imported component object.
Vue Async Components
We can take this a step further and not only enable dynamic components but split the application into smaller chunks and load the components over the network when it’s needed. This is done by using defineAsyncComponent and creating a wrapper that only calls the loader function when it’s needed on the page.
<script setup> import { defineAsyncComponent } from 'vue' const AdminPage = defineAsyncComponent(() => import('./components/AdminPageComponent.vue') ) </script> <template> <AdminPage /> </template>
In the example above, we’re able to efficiently implement component lazy-loading with only a few lines of code. This could be used to conditionally load a competent that isn’t needed when the application loads or that only a subset of users need.
Wrapping up
In this blog post, we explored essential concepts related to Vue components. Components are key components (😉) in Vue and many other frameworks. By understanding these concepts, you can create modular, reusable, and maintainable Vue applications. Happy coding!