Spring Sale 🍃 is live! Get 20% off ANY package with the coupon SPRINGBREAK

Vue 3.4: Slam Dunk

Vue 3.4 features some significant internal improvements, including a rewritten template parser, which now operates twice as fast. Additionally, the reactivity system has been refactored for greater accuracy and efficiency in effect triggering.

Advanced Inertia includes the source code of Mixjobs, a production-ready Laravel application, powered by Vue 3.4 and Inertia.js. Use the code SLAMDUNK to get 30% off any package.

This version also introduces several QoL improvements to the API to bring a better DX. This includes a stable version of defineModel and a new shorthand for binding props with the same name.

This post highlights the most important features introduced with the recent release. For more info, check out the official press release or the full changelog on GitHub.

Dependency Updates

When upgrading to 3.4, it is recommended to also update the following dependencies:

  • volar / vue-tsc@^1.8.27
  • @vitejs/plugin-vue@^5.0.0
  • vue-loader@^17.4.0 (if using webpack or vue-cli)
  • nuxt@^3.9.0 (if using Nuxt)

defineModel macro is now stable

The previously introduced experimental feature has been promoted to stable. Basically, this means that you don't need to declare a prop, an update event, and emit that event manually anymore.

<script setup>
const value = defineModel()
</script>
 
<template>
<input v-model="value" />
</template>

Compare this to the previous approach. You can save up to 10 lines when declaring a two-way bound prop.

<script setup>
const props = defineProps<{
modelValue: string
}>()
 
const emit = defineEmits<{
(e: "update:modelValue", value: string): void
}>()
 
const value = computed({
get: () => props.modelValue,
set: (value) => emit("update:modelValue", value),
})
</script>
 
<template>
<input v-model="value" />
</template>

You don't need any extra configuration, as defineModel now works out-of-the-box.

defineModel modifiers

Another notable change is that the defineModel macro brings a built-in way to handle modifiers.

For example, you want the model value to always be returned in uppercase in some certain components, but keep the input as-is in other cases.

You can achieve this by destructuring the return value of defineModel:

<script setup>
const [modelValue, modelModifiers] = defineModel({
set(value) {
// if the .uppercase modifier is used, return transformed value
if (modelModifiers.uppercase) {
return value.toUpperCase()
}
// otherwise, return the value as-is
return value
}
})
</script>

To activate the modifier, you just need to append the .uppercase part to the v-model declaration:

<template>
<!-- the `name` value will always be in uppercase regardless of the input value -->
<TextInput v-model.uppercase="name" />
 
<!-- the `email` value will be as-is -->
<TextInput v-model="email" />
</template>

v-bind shorthand syntax

Vue 3.4 also introduces a tiny quality-of-life feature. It is now possible to use a shorthand syntax for binding values when the key and value share the same name.

<script setup>
defineProps(['user'])
</script>
 
<template>
<!-- after -->
<UserCard :user />
 
<!-- before -->
<UserCard :user="user" />
</template>

Previous computed value

Similar to watch, computed properties now provide the previous value as the first parameter of the getter function.

The feature could be useful when you need to reference the previous state when generating a new value.

<script setup>
const count = ref(0)
 
const value = computed((previous) => {
if(previous) {
return previous * count.value;
}
 
return count.value;
})
 
// `value` produces 2
count.value = 2;
 
// `value` produces 6
count.value = 3;
</script>

watch once

A watcher can now only be fired once with the new option. When a tracked value is changed, the watcher will stop working.

<script setup>
const value = defineModel()
 
watch(value, () => {
console.log('The model value has been set')
}, { once: true })
</script>

This could be useful when you need to set up an initial state when some value is changed for the first time.

Props validation

In Vue 3.4, the validator function also provides all props as the second argument. This means that you can validate a prop value against other props.

<script setup>
defineProps({
min: {
type: Number,
default: 0,
},
max: {
type: Number,
required: true,
validator: (value, props) => value >= props.min,
}
})
</script>

In this example, if you try providing max lower than min (or 0 if using defaults), you will get a runtime warning.

Use Inertia.js like a boss

Learn advanced concepts and make apps with Laravel and Inertia.js a breeze to build and maintain.

© 2023 Boris Lepikhin. All rights reserved.