Introduction
As someone who loves Vuetify and formally a contributor. I wanted to dive back in and see all the amazing changes the made in this newest version over the past years. My first thought was to dive into one of my long time favorite and most used component V-Card.
Overview
Diving into the Vuetify Component Library can be overwhelming. I decided to break down the V-Card component into a 3 areas I wanted to explore.
- How does it get exported for use?
- How is the component created?
- How am I able to use the props/attributes available in this component?
How does it get exported for use?
When we install Vuetify into our application we are able to import the V-Card like this:
import { VCard } from 'vueitfy';
A line many of us are familiar with. How does this work and where do all of the exports for other components? The flow of exporting the card component is as follows:
Once the component is exported to the index.ts
of the /components
folder. Vuetify runs a build script (yarn build
) to package it. This is where the magic happens. The build script uses rollup to bundle the component and other parts of vuetify into a single folder for distribution. This dist
folder is what we use/refrence when we import Vuetify into our application.
(Results of yarn build
)
This is how Vuetify we go from a component being defined in the Vuetify codebase to being packages and exposed to us for use in our application.
How Is It Created?
Diving into the codebase, I found the V-Card component in: vuetify>src>components>VCard
The first thing I noticed was the VCard.tsx
file. This is the main file orchestrating the composition and use of other components within the same directory. Inside the VCard.tsx
file we see the various components being imported and used throughout the file.
// Components
import { VCardActions } from './VCardActions'
import { VCardItem } from './VCardItem'
import { VCardText } from './VCardText'
import { VDefaultsProvider } from '@/components/VDefaultsProvider'
import { VImg } from '@/components/VImg'
If we jump to line 50 & 51 we'll see exactly where VCard
is defined as a component and exported for use.
export const VCard = genericComponent<VCardSlots>()({
name: 'VCard',
// more code....
})
Why use this instad of the standard defineComponent as typically used in Vue applicaitons? example below:
export const VCard = defineComponent({
name: 'VCard',
// more code....
})
TLDR: genericComponent
essentially for the use of generic props and slots when defining components.
view here for more details.
Jumping to line 110 we are able to see exactly where the VCard componnet is initiating rendering and line 121 is where we start builing the components template.
line 110
useRender(() => {
// more code.....
})
useRender
is a custom rendering function that essentially does the following:
- Gets the current instance of the Node
- Checks if it's called inside a "setup" function
- If it is, it returns the view model
- It takes the valid view model and uses 'vue 3'
.render
function to return the valid vue model as a Virtual DOM tree to be added to our applications VDOM Tree.
line 121
return (
// building our component....
)
Parts of the Component.
The component can be broken down into several parts:
- Shell: Where we see the
<Tag></Tag>
component encapsulating the innards of V-Card - Layout: The parts of the components that render conditially based upon the variables with the prefix "has" and slots (ex. hasImage, hasText, slots.action, slots.image, ect.)
- Props/Options: The attributes/props section is where the default options are declared to interact/modify the V-Card Component. ex.(onClick, title, subtitle, actions, ect.)
- Classes: Where the component inherits both V-Card specific CSS classes and globally configured classes.
Shell
Inside the return statement on line 122 we see <Tag>
. What exactly is this "Tag" component? The <Tag>
component is essentially an agnostic component that inherits the name of the defined component's name property.
(Fact check. Looks likes it's a div by default.)
Layout
Inside the shell the layout is defined. There are a multitude of ternary statements determining what to render. An example of this is on line 151:
{ hasImage && (
<VDefaultsProvider
key="image"
defaults={{
VImg: {
cover: true,
src: props.image,
},
}}
>
<div class="v-card__image">
{ slots.image?.() ?? <VImg /> }
</div>
</VDefaultsProvider>
)
}
This statement is determining if there's an image if there is we render it! tag is rendered inside the div with v-card__image
styling. Throughout the rest of the card component we have a very similar pattern for card item, text, actions, ect. This is great because it allows for a clean/modular way to identify and render parts of the component. If we wanted to add our own custom part we'd follow the exact pattern.
How am I able to use the props & attributes in this component?
Using props and attributes in the card component is quite straight forward. Let's look at the flat prop. In the <VCard />
component we use the prop flat to remove the card shadow and border.
Example:
<v-card flat />
Inside the VCard
component we accept a boolean prop called flat line 59.
props: {
appendAvatar: String,
appendIcon: IconValue,
disabled: Boolean,
flat: Boolean, // here
hover: Boolean,
image: String,
// more code.....
Next we go inside our base of the VCard
component which is <Tag />
and check two conditions
'v-card--flat': props.flat,
- we apply the'v-card--flat
class to our card component ifprops.flat
istrue
'v-card--hover': props.hover && !(props.disabled || props.flat),
- we apply the'v-card--hover'
class to our card component ifprops.hover
is true AND we don't have our disabled prop or flat prop true. Since we do have a flat prop the'v-card--hover'
class isn't applied.
With this we went from <v-card flat />
to checking if that prop exists. If it does exist we want to do a certain thing within our Vuetify component. Very similar to how props are used building out component in vue or any other web component framework. Not too bad right? 🙂
Overall the VCard
component along with many others in Vuetify are not as scary to look into once a foothole is found. The general paradigms we see are quite similar to how many people would build vue components.
Outside of custom components such as <Tag />
the pattern that's most prevalent and heavily emphasized in this library emphasis is composability. When looking further into the component you'll see many imported composables used to make the VCard
component what it is. Hopefully this helps and you feel more comfortable diving into the internals of Vuetify! 😃