Would you like to buy me a ☕️ instead?
Note: This is the third part of my “Advanced Vue.js Application Architecture” series on how to structure and test large scale Vue.js applications. Stay tuned, there’s more to come! Follow me on Twitter if you don’t want to miss the next article.
<< First < Previous Next >
Today we’ll take a look at how we can build renderless Vue.js components which make it possible to react to lifecycle hooks and route changes directly in the template section of a component.
If you are curious about the result, you can see the final outcome of what we build on CodeSandbox.
Lifecycle hooks
Let’s take a look at how we can build a simple renderless component for handling lifecycle hooks.
// src/components/FrameHooks.vue
export default {
created() {
// As soon as this component is created, it's parent component
// must have already been created, so we immediately trigger the hook.
this.$emit('created');
const triggerMounted = () => this.$emit('mounted');
this.$parent.$on('hook:mounted', triggerMounted);
const triggerUpdated = () => this.$emit('updated');
this.$parent.$on('hook:updated', triggerUpdated);
this.$once('hook:beforeDestroy', () => {
this.$parent.$off('hook:mounted', triggerMounted);
this.$parent.$off('hook:updated', triggerUpdated);
});
},
render() {
// Render the first child of the default slot.
return this.$slots.default[0];
},
};
In the following example you can see how we can use our newly created FrameHooks
component in combination with a renderless ArticleListFrame
component to trigger an API request for fetching a list of articles.
<template>
<ArticleListFrame
v-slot="{ articles, methods: { fetchList } }"
>
<FrameHooks @created="fetchList">
<ul class="ArticleList">
<li v-for="article in articles">
{{ article.title }}
</li>
</ul>
</FrameHooks>
</ArticleListFrame>
</template>
As you can see above, the FrameHooks
component is especially useful for consuming methods provided by other renderless components. This is only one of many possible use cases for how to utilize the FrameHooks
component.
Do you want to learn more about building advanced Vue.js applications?
Register for the Newsletter of my upcoming book: Advanced Vue.js Application Architecture.
Route changes
Another area where it can be very useful to use hooks to react to certain changes in the app is routing.
// src/components/FrameHooks.vue
export default {
watch: {
$route: {
handler(current, prev) {
const currentQueryString = JSON.stringify(current.query);
const prevQueryString = JSON.stringify(prev.query);
const queryChanged = currentQueryString !== prevQueryString;
if (queryChanged) {
this.$emit('route-query-change', current.query);
}
},
},
},
created() {
// ...
},
// ...
};
Next you can see how we can use the route-query-change
hook to call the fetchList()
method every time a query parameter of the route changes. If for example the route changes from /articles?page=1
to /articles?page=2
the fetchList()
method is called and the articles for page 2
are fetched.
<template>
<ArticleListFrame
v-slot="{
articles,
methods: { fetchList }
}"
>
<FrameHooks
@created="fetchList({ page: $route.query.page });"
@route-query-change="fetchList({ page: $event.page });"
>
<div class="Home">
<ul class="ArticleList">
<li v-for="article in articles" :key="article.title">
{{ article.title }}
</li>
</ul>
<RouterLink
:to="{ query: { page: 1 } }"
>
Page 1
</RouterLink> |
<RouterLink
:to="{ query: { page: 2 } }"
>
Page 2
</RouterLink>
</div>
</FrameHooks>
</ArticleListFrame>
</template>
I think this simple example already demonstrates very well how powerful this pattern can be. We can reuse existing Frame Components throughout our application to integrate something like pagination functionality without even writing a single line of custom JavaScript code (not counting the function call).
Wrapping it up
By using the renderless component pattern, to build wrapper components which provide access to hook events directly in the template section of our components, we can build components which are more transparent, easier to understand and highly reusable.
However, there are also a number of things to consider: if you’re using a lot of renderless components you can run into the problem that you have to nest multiple renderless components. This can be problematic in certain cases. Therefore you should aim for building very simple components to keep the number of renderless components required to build a single component at a minimum.