🔥 (#138) h and Render Functions, the Hybrid API, and Template Refs
Hey all!
Clean Components Toolkit will be launched on November 22!
That's just two weeks away, so mark your calendar and don't forget.
I'll be doing an awesome launch sale, so that will be the best time to get it.
To celebrate, I've included a bonus tip and a bonus video where I give you a peek behind the curtain at how the code diffing and refactoring system works.
It's a really cool use of Nuxt Content, and it took quite a bit for me to figure out — but it's totally worth it!
Also, tomorrow is VueConf Toronto, so I'm laser focused on that this week.
I'll make my slides available for you, so follow me on Twitter/X because that's where I'll be sharing them.
As always, here are your tips!
— Michael
🔥 (Bonus Video): Refactoring Steps Behind The Scenes
This is maybe my favourite thing I've ever built with code.
It lets me write in a custom Markdown format so I can provide you with a really awesome educational experience.
You can easily see how the code is changing from step to step, going at your own pace and jumping around as needed.
In the video I explain how I did it and why I did it:
🔥 (Bonus Tip): The Data Store Pattern
This is a simplified version of a tool included in Clean Components Toolkit.
The simplest solution to lots of state management problems is to use a composable to create a shareable data store.
This pattern has a few parts:
- A global state singleton
- Exporting some or all of this state
- Methods to access and modify the state
Here's a simple example:
import { reactive, toRefs, readonly } from 'vue'; import { themes } from './utils'; // 1. Create global state in module scope, shared every // time we use this composable const state = reactive({ darkMode: false, sidebarCollapsed: false, // 2. This theme value is kept private to this composable theme: 'nord', }); export default () => { // 2. Expose only some of the state // Using toRefs allows us to share individual values const { darkMode, sidebarCollapsed } = toRefs(state); // 3. Modify our underlying state const changeTheme = (newTheme) => { if (themes.includes(newTheme)) { // Only update if it's a valid theme state.theme = newTheme; } } return { // 2. Only return some of the state darkMode, sidebarCollapsed, // 2. Only expose a readonly version of state theme: readonly(state.theme), // 3. We return a method to modify underlying state changeTheme, } }
🔥 h and Render Functions
When using the render
function instead of templates, you'll be using the h
function a lot:
<script setup> import { h } from 'vue'; const render = () => h('div', {}, 'Hello Wurld'); </script>
It creates a VNode (virtual node), an object that Vue uses internally to track updates and what it should be rendering.
The first argument is either an HTML element name or a component (which can be async if you want):
<script setup> import { h } from 'vue'; import MyComponent from './MyComponent.vue'; const render = () => h(MyComponent, {}, []); </script>
The second argument is a list of props, attributes, and event handlers:
<script setup> import { h } from 'vue'; import MyComponent from './MyComponent.vue'; const render = () => h(MyComponent, { class: 'text-blue-400', title: 'This component is the greatest', onClick() { console.log('Clicked!'); }, }, []); </script>
The third argument is either a string for a text node, an array of children VNodes, or an object for defining slots:
<script setup> import { h } from 'vue'; import MyComponent from './MyComponent.vue'; const render = () => h(MyComponent, {}, [ 'Simple text node', h('span', {}, 'Text inside of a <span> element'), ]); </script>
These render functions are essentially what is happening "under the hood" when Vue compiles your single file components to be run in the browser.
But by writing out the render function yourself, you are no longer constrained by what can be done in a template.
You have the full power of Javascript at your fingertips 🖐
This is just scratching the surface on what render functions and h
can do. Read more about them on the official docs.
🔥 Hybrid API: Composition API + Options API
You don't have to decide between Options API and Composition API, you can use both:
export default { setup() { const darkMode = ref(false); return { darkMode } }, methods: { saveDarkMode() { localStorage.setItem('dark-mode', this.darkMode); }, } };
Although you can access Composition API from the Options API, it's a one-way street. The Composition API cannot access anything defined through the Options API:
export default { setup() { const darkMode = ref(false); return { darkMode } }, methods: { changeTheme(val) { // This WILL NOT access the ref defined above this.darkMode = val; } } };
This can be useful for incremental adoption of the Composition API, because it lets you use reusable composables in your Options API.
But mixing two styles of writing components is likely to cause more headaches than it solves, so please think twice before doing this!
🔥 Template Refs
We can use template refs to directly access elements in our template:
<template> <div> <input type="text" placeholder="Start typing..." ref="input" /> </div> </template>
When using the composition API, we provide the ref, and Vue will set the value to the element of the same name:
<script setup> import { ref, onMounted } from "vue"; onMounted(() => { // Once mounted it's assigned the DOM element input.value.focus(); }); // Initially set to null const input = ref(null); </script>
If we're using the options API it's a bit different. Instead of creating a ref
ourselves, we have to access the template ref through a special $refs
object:
<script> export default { mounted() { // Access our input using template refs, then focus this.$refs.input.focus() } } </script>
📜 Options Object Pattern
The Options Object Pattern is a great way to make your composables more flexible and easier to use.
In this article I explore the pattern, how it works, and some edge cases to consider.
Check it out here: Options Object Pattern
💬 Achieving perfection
"When debugging, novices insert corrective code; experts remove defective code." — Richard Pattis
🧠 Spaced-repetition: Writable Computed Refs
The best way to commit something to long-term memory is to periodically review it, gradually increasing the time between reviews 👨🔬
Actually remembering these tips is much more useful than just a quick distraction, so here's a tip from a couple weeks ago to jog your memory.
Computed refs are cool and all, but did you know you can create writable computed refs?
const firstName = ref(''); const lastName = ref(''); const fullName = computed({ get: () => `${firstName.value} ${lastName.value}`, set: (val) => { const split = val.split(' '); // ['Michael', 'Thiessen'] firstName.value = split[0]; // 'Michael' lastName.value = split[1]; // 'Thiessen' } }); fullName.value = 'Michael Thiessen'; console.log(lastName.value); // 'Thiessen'
p.s. I also have four courses: Vue Tips Collection, Mastering Nuxt 3, Reusable Components and Clean Components
评论
发表评论