SoFunction
Updated on 2025-04-12

One article will help you master the common features of Vue3.5

Responsive Props Destruction

Props in Vue3.5 officially supports deconstruction and adds responsive tracking

Set default values

Declare props default values ​​using JavaScript native default value syntax

before

const props = withDefaults(
  defineProps<{
    count?: number
    msg?: string
  }>(),
  {
    count: 0,
    msg: 'hello'
  }
)

Now

const { count = 0, msg = 'hello' } = defineProps<{
  count?: number
  message?: string
}>()

Responsive destruction

When accessing variables deconstructed by defineProps in the same <script setup> code block, the Vue compiler will automatically add props in front of it

before

const { foo } = defineProps(['foo'])
 
watchEffect(() =&gt; {
  // Run only once before 3.5  (foo)
})

Now

const { foo } = defineProps(['foo'])
 
watchEffect(() =&gt; {
  // Re-execute in 3.5 when the "foo" prop changes  (foo)
  // `foo` is converted from the compiler to ``, the above is equivalent to `()`})

Similarly, listening to a deconstructed prop variable or passing it into a composable item while preserving responsiveness requires wrapping it in a getter

before

const { foo } = defineProps(['foo'])
 
watch(foo, /* ... */)

Now:

// watch(foo, /* ... */) is equivalent to watch(, ...). We pass a value to watch instead of a responsive data sourcewatch(() =&gt; foo, /* ... */)
 
// Pass the deconstructed prop to the external function and maintain responsivenessuseComposable(() =&gt; foo)

Listening (watch/watcheffect) related

watch supports specified depth: number

The deep option of watch now supports passing in numbers to specify the depth of the listener

const state = ref({
  a: {
    b: {
      c: 1
    }
  }
})
 
watch(state, (newValue) =&gt; {
    (`state: ${newValue}`)
  },
  { deep: 2 }
)
 
 = { c: 2 } // Change the properties of the second layer to trigger listening = 2 // The properties of the third layer have been changed and listening is not triggered

Cleaning function onWatcherCleanup / onEffectCleanup

In the past, when we wanted to send an asynchronous request in the listening function, the request parameters were likely to change. At this time, we need to set up the global variable storage AbortController and clean it before the component is uninstalled.

import { watch, onBeforeUnmount } from "vue"
 
let controller = new AbortController()
 
watch(state, (newValue) =&gt; {
    () // Cancel the last request    controller = new AbortController()
 
    fetch(`/api/${newValue}`, { signal:  }).then(() =&gt; {
    // Callback logic  })
});
 
// Clean up the components before uninstallingonBeforeUnmount(() =&gt; ())

Now that we have the cleaning function onWatcherCleanup / onEffectCleanup, we can call it directly to clean up the previous call (asynchronous) function/request

import { watch, onWatcherCleanup } from 'vue'
 
watch(id, (newId) =&gt; {
  const controller = new AbortController()
 
  fetch(`/api/${newId}`, { signal:  }).then(() =&gt; {
    // Callback logic  })
 
  onWatcherCleanup(() =&gt; {
    // Terminate expiration request    ()
  })
})

The onEffectCleanup function is written similar to the above, the difference is the import source

import { onEffectCleanup } from "@vue/reactivity";

[!WARNING]
onWatcherCleanup is only supported in Vue 3.5+ and must be called during synchronous execution of watchEffect effect function or watch callback function: you cannot call it after the await statement of the asynchronous function.

watch return value enhancement

The watch return value has been added to the pause/restoring listener, which can control the listening range more carefully

const { stop, pause, resume } = watch(() =&gt; {})
 
// Pause the listenerpause()
 
// Recover laterresume()

SSR Improvements

Lazy Activation Lazy Hydration

Asynchronous components can control when activation is performed through the hydrate option in the defineAsyncComponent() API

Activate when free

import { defineAsyncComponent, hydrateOnIdle } from 'vue'
 
const AsyncComp = defineAsyncComponent({
  loader: () =&gt; import('./'),
  hydrate: hydrateOnIdle(/* Pass optional maximum timeout */)
})

Activate when the element becomes visible

import { defineAsyncComponent, hydrateOnVisible } from 'vue'
 
const AsyncComp = defineAsyncComponent({
  loader: () => import('./'),
  hydrate: hydrateOnVisible()
})

Customize the policy

import { defineAsyncComponent, type HydrationStrategy } from 'vue'
 
const myStrategy: HydrationStrategy = (hydrate, forEachElement) =&gt; {
  // forEachElement is a helper function that traverses all root elements in the DOM that is not activated by the component.  // Because the root element may be a fragment rather than a single element  forEachElement(el =&gt; {
    // ...
  })
  // Call `hydrate` when ready`  hydrate()
  return () =&gt; {
    // Return a destroy function if necessary  }
}
 
const AsyncComp = defineAsyncComponent({
  loader: () =&gt; import('./'),
  hydrate: myStrategy
})

other

Please checkVue3 official documentation - lazy activation, I won't repeat it here

useId() Generates a unique application ID

Used to generate an in-app unique ID for accessibility attributes or form elements. In our daily applications, we can mainly solve the problem of different ids generated by the server and client, resulting in rendering errors caused by different renderings

<script setup>
import { useId } from 'vue'
 
const id = useId()
</script>
 
<template>
  <form>
    <label :for="id">Name:</label>
    <input : type="text" />
  </form>
</template>

data-allow-mismatch

If the client value inevitably differs from its server-side corresponding value (such as date), we can use the property data-allow-mismatch to avoid the resulting activation mismatch warning

<span data-allow-mismatch>{{ () }}</span>

You can also specify specific types. Allowed values ​​are: text, children (only allow direct child components to mismatch), class, style, attribute

other

useTemplateRef()

Return a shallow ref, which can bind elements more intuitively, and also supports dynamic binding.

<script setup>
import { ref, useTemplateRef, onMounted } from 'vue'
 
const targetRef = ref('input1')
const inputRef = useTemplateRef<HTMLInputElement>()
 
onMounted(() => {
  ()
})
</script>
 
<template>
  <input ref="input1" />
    <input ref="input2" />
</template>

I won't explain other things that are not commonly used

This article about this article about Vue3.5's common features will be introduced here. For more relevant content on Vue3.5, please search for my previous articles or continue browsing the related articles below. I hope you will support me in the future!