[Vue] One Object to Rule Them All (v-bind & v-on syntax)
Introduction
One of the first things that Vue developers often learn is the magic of using v-bind and v-on to supercharge their HTML with dynamic bindings and event listeners. The declarative nature of v-bind and v-on make it very easy for other developers to follow.
<input
:id="inputId"
:data-tooltip="tooltipText"
@change="updateForm"
@mouseover="showTooltip"
/>
However, as components grow larger and complex, we can find that this approach becomes limiting and can even create confusion. When it comes to this point, it’s time to learn a hidden secret that most Vue developers don’t know about till much later in their journey: you can dynamically define multiple values for v-bind and v-on.
v-bind with No Argument
As I mentioned earlier, most Vue developers are familiar with the following syntax:
<template>
<img v-bind:src="imageAttrs.src" v-bind:alt="imageAttrs.text" />
</template>
<script>
export default {
data() {
return {
imageAttrs: {
src: '/vue-mastery-logo.png',
text: 'Vue Mastery Logo'
}
}
}
}
</script>
What many don’t realize is that this syntax is the href part of our v-bind statement is actually an argument. In other words, by passing in an argument to v-bind, we are telling Vue that we want to bind a specific property to this particular attribute.
However, what’s even more perplexing is that this argument is optional. What does this mean? Well, believe it or not, our code can actually be written instead as:
<img v-bind="{ src: imageAttrs.src, alt: imageAttrs.text }" />
This could then be abstracted one step further to:
<img v-bind="imageAttrs" />
However, while this this is very short and concise, most of us would still prefer the original way of declaring attributes due to its clarity and ease of use.
v-on with No Arguments
When it comes to v-on, most people are familiar with the following usage:
<template>
<img v-on:click="openGallery" v-on:mouseover="showTooltip" />
</template>
<script>
export default {
methods: {
openGallery() { ... },
showTooltip() { ... }
}
}
</script>
Just like v-bind, the event name we define is actually an argument being passed to v-on. As a result, we can also rewrite our input element as:
<template>
<img v-on="{ click: openGallery, mouseover: showTooltip }" />
</template>
And theoretically, we could abstract this one step further to:
<template>
<img v-on="inputEvents" />
</template>
<script>
export default {
computed: {
inputEvents: {
click: this.openGallery,
mouseover: this.showTooltip
}
},
methods: {
openGallery() { ... },
showTooltip() { ... }
}
}
</script>
However, just like we mentioned earlier in the v-bind section, most of us would still prefer the original syntax because it is easy to understand and change if necessary.
When would this be useful?
As we illustrated in our earlier examples, simple components with an easy to understand structure has little reason to use an abstracted syntax. However, as our components become more complex and difficult to understand, our newly acquired technique suddenly has a lot of applications.
NewsFeed Scenario
For example, let’s consider the scenario where you are building a NewsFeed component where the API will pass a series of components that changes based on what the editors want. This means that sometimes you will get NewsArticle components, but sometimes you’ll get NewsAd components.
When it comes to implementing this, you may start out with something like the following:
<template>
<main>
<Component
v-for="content in apiResponse"
:key="content.id"
:is="content.type"
:article-title="content.title"
:article-content="content.body"
:ad-image="content.image"
:ad-heading="content.heading"
@click="content.type === 'NewsArticle' ? openArticle : openAd"
@mouseover="content.type === 'NewsArticle' ? showPreview : trackAdEvent"
/>
</main>
</template>
In this scenario, we are:
- Using the
Componentcomponent to dynamically render either theNewsArticleorNewsAdcomponent using theisattribute that is defined withcontent.type. - Binding dynamic attributes for the
NewsArticlecomponent:article-titleandarticle-content - Binding dynamic attributes for the
NewsAdcomponent:ad-imageandad-heading
And thought it doesn’t look too confusing yet, it is clear that certain props and events only matter in depending on what component it is. As this list of attributes and events becomes much longer, this will become exponentially more difficult to mange and understand.
Refactoring NewsFeed
When a component becomes difficult to understand and manage, this is the time for us to seek out new techniques and abstractions. Using our newly acquired techniques, let’s refactor the NewsFeed component.
<template>
<main>
<Component
v-for="content in apiResponse"
:key="content.id"
:is="content.type"
v-bind="feedItem(content).attrs"
v-on="feedItem(content).events"
/>
</main>
</template>
<script>
export default {
methods: {
feedItem(item) {
if (item.type === 'NewsArticle') {
return {
attrs: {
'article-title': item.title,
'article-content': item.content
},
events: {
click: this.openArticle,
mouseover: this.showPreview
}
}
} else if (item.type === 'NewsAd') {
return {
attrs: {
'ad-image': item.image,
'ad-heading': item.heading
},
events: {
click: this.openAd,
mouseover: this.trackAdEvent
}
}
}
}
}
}
</script>
And with that, our NewsFeed article has properly delegated the responsibilities to the specific components rather than mix them up under a single HTML element.

浙公网安备 33010602011771号