Vue.js is an easy to use web app framework that we can use to develop interactive front end apps.
In this article, we’ll look at how Vue components can play nice with each other.
Assign Attributes to the Right Elements
We can pass attributes from a grandparent component to a grandchild component by setting the inheritAttrs
option to false
, and then add v-bind='$attrs'
to the child of the grandparent to pass the attributes of the grandparent to the grandchild.
For instance, we can write the following code to do that:
index.js
:
Vue.component("foo", {
inheritAttrs: false,
template: `
<p v-bind='$attrs'>foo</p>
`
});
new Vue({
el: "#app"
});
index.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<title>App</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<foo class="foo"></foo>
</div>
<script src="index.js"></script>
</body>
</html>
Then the foo
class will be applied to the p element in foo
since we set inheritAttrs
to false
in foo
.
Use Browser Norms for Keyboard Navigation
We should follow the conventions for keyboard shortcuts for common operations if we’re going to handle keyboard shortcuts.
For instance, the tab key lets us move to different form fields. Enter is for submitting data, etc.
Use Events Over Callbacks
Emitting events from child to parent is better than callback functions that are passed in from parent to child.
For instance, we have to pass callbacks as props the following way:
index.js
:
Vue.component("foo", {
props: ["showAlert"],
methods: {
displayAlert() {
if (typeof this.showAlert === "function") {
this.showAlert("foo");
}
}
},
template: `
<button @click='displayAlert'>Click Me</button>
`
});
new Vue({
el: "#app",
methods: {
showAlert(text) {
alert(text);
}
}
});
index.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<title>App</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<foo :show-alert="showAlert"></foo>
</div>
<script src="index.js"></script>
</body>
</html>
In the code above, we have the show-alert
prop, which we pass the showAlert
method from the root instance to the child. This is more complex and error-prone because we have to make sure that we passed in a function as the value of the prop.
We have to check that by using typeof
of Vue’s built-in prop type validation.
Then we have to call the function that’s passed in as the value of the prop as we did in the displayAlert
function. The name is different so it won’t clash with the showAlert
prop.
On the other hand, if we emit events, then we won’t have to pass functions from parent to child to run something in the parent when something happens in the child.
For example, we can rewrite the example above by writing:
index.js
:
Vue.component("foo", {
template: `
<button @click='$emit("button-clicked", "foo")'>Click Me</button>
`
});
new Vue({
el: "#app",
methods: {
showAlert(text) {
alert(text);
}
}
});
index.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<title>App</title>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
<div id="app">
<foo @button-clicked="showAlert($event)"></foo>
</div>
<script src="index.js"></script>
</body>
</html>
The code above is a lot simpler. We just call $emit
with the event name as the first argument and the payload that we want to send to the parent as the second.
Then we can listen to the button-clicked
event from the parent as we did in index.html
by writing:
@button-clicked="showAlert($event)"
Then $event
object has the payload, which is 'foo’
, so we can pass it straight into the showAlert
method. Finally, we’ll see the alert display when we click the button.
Limit In-Component Styles
Scoped in-component styles in single-file components are only applied to the component itself. If we’re reusing it in many locations or apps, then we should let the styles be in that app instead so that we won’t have conflicts of styles between our component and the styles that’s already in the app.
This makes styling reusable components hard since we’ve to deal with conflicts.
Therefore, we should either leave the styles out of the single-file component or have the ability to toggle them on and off.
For instance, we can toggle the styles with an is-styled
prop as follows:
HelloWorld.vue
:
<template>
<div>
<p :class="{'is-styled': isStyled}">{{ msg }}</p>
</div>
</template>
<script>
export default {
name: "HelloWorld",
props: {
msg: String,
isStyled: Boolean
}
};
</script>
<style scoped>
.is-styled {
color: #42b983;
}
</style>
App.vue
:
<template>
<div id="app">
<button @click="isStyled = !isStyled">Toggle Style</button>
<HelloWorld msg="Hello Vue" :is-styled="isStyled"/>
</div>
</template>
<script>
import HelloWorld from "./components/HelloWorld";
export default {
name: "App",
components: {
HelloWorld
},
data() {
return {
isStyled: true
};
}
};
</script>
In the code above, we have :class
to bind the class dynamically according to the value of the isStyled
prop that we passed in in HelloWorld.vue
Then in App.vue
, we can set the isStyled
value and pass it into the is-styled
prop as the value so that we can have the ability to turn our HelloWorld
‘s component’s styles on and off as we wish.
Conclusion
We can use v-bind='$attrs'
to inherit attributes from parent to child. If we’re going to add keyboard shortcuts handling to our app, we should following the conventions of other apps to make using our app convenient.
Also, we should emit events instead of calling callbacks passed from parent to child.
Finally, if we have shared components, we should either leave out the styles or have the ability to toggle them on and off.