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 valueif (modelModifiers.uppercase) {return value.toUpperCase()}// otherwise, return the value as-isreturn 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 2count.value = 2;Â// `value` produces 6count.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.