Torna al blog

Vue 3.5: Aggiornamenti e Miglioramenti Performance

Scopri Vue.js 3.5 Tengen: Reactive Props Destructure, ottimizzazioni SSR, nuovo useTemplateRef, miglioramenti Teleport e performance eccezionali.

Edoardo Midali

Edoardo Midali

Developer · Content Creator

8 min di lettura
Vue 3.5: Aggiornamenti e Miglioramenti Performance

Vue 3.5 "Tengen" è stato rilasciato il 3 settembre 2024 con focus su performance, developer experience e nuove API reattive. Questa release introduce Reactive Props Destructure, ottimizzazioni significative per SSR e diversi miglioramenti all'API Composition.

🎯 Novità Principali

Reactive Props Destructure

Finalmente è possibile destrutturare props mantenendo la reattività:

<script setup>
// ❌ Vue 3.4 - perde reattività
const { msg, count } = defineProps(['msg', 'count']);

watch(() => msg, (newMsg) => {
  // Non si triggera mai! msg non è reattivo
  console.log(newMsg);
});

// ✅ Vue 3.5 - mantiene reattività!
const { msg, count } = defineProps(['msg', 'count']);

watch(() => msg, (newMsg) => {
  // Funziona perfettamente!
  console.log(newMsg);
});

// Con TypeScript
interface Props {
  msg: string;
  count: number;
  disabled?: boolean;
}

const { msg, count, disabled = false } = defineProps<Props>();

// Default values con destructuring
const { title = 'Default Title', items = [] } = defineProps<{
  title?: string;
  items?: string[];
}>();
</script>

<template>
  <div>
    <h1>{{ msg }}</h1>
    <p>Count: {{ count }}</p>
    <button :disabled="disabled">Click</button>
  </div>
</template>

Vantaggi:

  • Codice più pulito e leggibile
  • Default values nel destructuring
  • Reattività completa mantenuta
  • Migliore DX con meno boilerplate

useTemplateRef() API

Nuovo modo per accedere ai template refs:

<script setup>
import { useTemplateRef, onMounted } from "vue";

// ❌ Vue 3.4 - ref manual
import { ref, onMounted } from "vue";

const inputRef = ref(null);

onMounted(() => {
  inputRef.value?.focus();
});

// ✅ Vue 3.5 - useTemplateRef
const inputRef = useTemplateRef("myInput");

onMounted(() => {
  inputRef.value?.focus();
});

// Con TypeScript
const inputRef = useTemplateRef < HTMLInputElement > "myInput";

onMounted(() => {
  inputRef.value?.select(); // Type-safe!
});

// Multiple refs
const buttonRefs = useTemplateRef < HTMLButtonElement > "buttons";
</script>

<template>
  <input ref="myInput" type="text" />

  <!-- Con v-for -->
  <button v-for="i in 5" :key="i" :ref="'buttons'">Button {{ i }}</button>
</template>

Ottimizzazioni SSR Drastiche

Vue 3.5 introduce miglioramenti massicci per SSR:

Performance Improvements:

  • 2x più veloce hydration
  • -56% memoria utilizzata durante SSR
  • Lazy hydration per componenti pesanti
<!-- app.vue -->
<script setup>
import { defineAsyncComponent } from "vue";

// Lazy hydration per componenti pesanti
const HeavyChart = defineAsyncComponent({
  loader: () => import("./HeavyChart.vue"),
  hydrate: "idle", // Hydrate quando idle
});

const VideoPlayer = defineAsyncComponent({
  loader: () => import("./VideoPlayer.vue"),
  hydrate: "visible", // Hydrate quando visibile
});

const InteractiveMap = defineAsyncComponent({
  loader: () => import("./InteractiveMap.vue"),
  hydrate: "interaction", // Hydrate al primo click
});
</script>

<template>
  <div>
    <h1>Dashboard</h1>

    <!-- Lazy hydration strategies -->
    <HeavyChart :data="chartData" />
    <VideoPlayer src="/video.mp4" />
    <InteractiveMap :markers="locations" />
  </div>
</template>

Teleport con defer

Teleport ora supporta rendering differito:

<script setup>
import { ref } from "vue";

const showModal = ref(false);
</script>

<template>
  <div>
    <button @click="showModal = true">Open Modal</button>

    <!-- defer: aspetta che #modal-target sia pronto -->
    <Teleport to="#modal-target" defer>
      <div v-if="showModal" class="modal">
        <h2>Modal Content</h2>
        <button @click="showModal = false">Close</button>
      </div>
    </Teleport>
  </div>

  <!-- Target può essere definito dopo -->
  <div id="modal-target"></div>
</template>

Benefici:

  • Nessun errore se target non esiste ancora
  • Ordine dichiarazione più flessibile
  • SSR-friendly

⚡ Miglioramenti Performance

Reactivity System Ottimizzato

// Vue 3.5 - reactivity engine migliorato
import { ref, computed, watch } from "vue";

// Performance migliorate per computed chains
const base = ref(10);
const doubled = computed(() => base.value * 2);
const quadrupled = computed(() => doubled.value * 2);
const octupled = computed(() => quadrupled.value * 2);

// Vue 3.5: 20-30% più veloce per computed complessi

Memory Usage Ridotto

<script setup>
// Vue 3.5 usa meno memoria per:
// - Large lists
// - Deep reactive objects
// - Computed chains
// - Watchers

const items = ref(
  Array.from({ length: 10000 }, (_, i) => ({
    id: i,
    name: `Item ${i}`,
    data: {
      /* nested data */
    },
  }))
);

// -30% memory usage vs Vue 3.4
</script>

Custom Elements Migliorati

// defineCustomElement ottimizzato
import { defineCustomElement } from "vue";
import MyComponent from "./MyComponent.vue";

const MyWebComponent = defineCustomElement(MyComponent);

customElements.define("my-component", MyWebComponent);

// Features Vue 3.5:
// - Shadow DOM ottimizzato
// - Props binding più veloce
// - Event handling migliorato

🔧 Nuove API e Miglioramenti

onWatcherCleanup()

Cleanup più pulito per watchers:

<script setup>
import { watch, onWatcherCleanup } from "vue";

const searchQuery = ref("");

watch(searchQuery, async (query) => {
  const controller = new AbortController();

  // Cleanup automatico quando watcher ri-esegue
  onWatcherCleanup(() => {
    controller.abort();
  });

  try {
    const response = await fetch(`/api/search?q=${query}`, {
      signal: controller.signal,
    });
    const data = await response.json();
    // Process data...
  } catch (err) {
    if (err.name === "AbortError") {
      console.log("Request aborted");
    }
  }
});
</script>

useId() per SSR

Genera ID unici SSR-safe:

<script setup>
import { useId } from "vue";

const id = useId(); // Consistente tra server e client

// Utile per accessibilità
const labelId = `label-${id}`;
const inputId = `input-${id}`;
</script>

<template>
  <div>
    <label :id="labelId" :for="inputId"> Username </label>
    <input :id="inputId" :aria-labelledby="labelId" />
  </div>
</template>

app.config.errorHandler Migliorato

// main.js
import { createApp } from "vue";
import App from "./App.vue";

const app = createApp(App);

app.config.errorHandler = (err, instance, info) => {
  // Vue 3.5: info più dettagliato
  console.error("Error:", err);
  console.log("Component:", instance?.$options.name);
  console.log("Error Info:", info); // lifecycle hook, event handler, etc.

  // Invia a error tracking
  if (import.meta.env.PROD) {
    trackError(err, { component: instance, info });
  }
};

app.mount("#app");

🎨 Composition API Enhancements

defineModel() Miglioramenti

<!-- ParentComponent.vue -->
<script setup>
import { ref } from "vue";
import ChildComponent from "./ChildComponent.vue";

const searchQuery = ref("");
const filters = ref({ category: "all", sort: "name" });
</script>

<template>
  <ChildComponent v-model="searchQuery" v-model:filters="filters" />
</template>

<!-- ChildComponent.vue -->
<script setup>
// Vue 3.5: defineModel più potente
const query = defineModel({ type: String, required: true });
const filters = defineModel("filters", {
  type: Object,
  default: () => ({ category: "all", sort: "name" }),
});

// Trasformazioni
const upperQuery = defineModel({
  get: (value) => value.toUpperCase(),
  set: (value) => value.toLowerCase(),
});
</script>

<template>
  <div>
    <input v-model="query" placeholder="Search..." />

    <select v-model="filters.category">
      <option value="all">All</option>
      <option value="tech">Tech</option>
    </select>
  </div>
</template>

defineOptions() Esteso

<script setup>
// Vue 3.5: più opzioni supportate
defineOptions({
  name: "MyComponent",
  inheritAttrs: false,
  customOptions: {
    // Custom options per plugin
  },
});

const props = defineProps({
  msg: String,
});
</script>

🔄 Come Aggiornare a Vue 3.5

Installazione

# npm
npm install vue@latest

# yarn
yarn add vue@latest

# pnpm
pnpm add vue@latest

# Verifica versione
npm list vue

Per Progetti Vite

# Aggiorna anche Vite plugin
npm install -D @vitejs/plugin-vue@latest

# vite.config.js
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';

export default defineConfig({
  plugins: [
    vue({
      // Abilita nuove features
      script: {
        defineModel: true,
        propsDestructure: true
      }
    })
  ]
});

TypeScript Setup

// tsconfig.json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "lib": ["ES2020", "DOM", "DOM.Iterable"],
    "jsx": "preserve",
    "moduleResolution": "bundler",
    "types": ["vite/client"]
  }
}

⚠️ Breaking Changes

1. Reactivity Transform Rimosso

<!-- ❌ Non più supportato -->
<script setup>
let count = $ref(0);
const doubled = $computed(() => count * 2);
</script>

<!-- ✅ Usa Composition API standard -->
<script setup>
import { ref, computed } from "vue";

const count = ref(0);
const doubled = computed(() => count.value * 2);
</script>

2. Async Component Type

// ❌ Vue 3.4
const AsyncComp = defineAsyncComponent(() => import("./Component.vue"));

// ✅ Vue 3.5 - type più specifico
import { defineAsyncComponent, type Component } from "vue";

const AsyncComp: Component = defineAsyncComponent(
  () => import("./Component.vue")
);

3. Custom Directives

Alcune signature interne cambiate - aggiorna custom directives se necessario.

🎓 Best Practices

1. Usa Props Destructure

<script setup>
// ✅ Pulito e reattivo
const { title, items = [], loading = false } = defineProps<{
  title: string;
  items?: Item[];
  loading?: boolean;
}>();
</script>

2. useTemplateRef per Refs

<script setup>
// ✅ Più esplicito e type-safe
const inputRef = useTemplateRef < HTMLInputElement > "input";
</script>

3. Lazy Hydration per SSR

<script setup>
// ✅ Migliori performance SSR
const HeavyComponent = defineAsyncComponent({
  loader: () => import("./Heavy.vue"),
  hydrate: "visible",
});
</script>

4. onWatcherCleanup per Async

<script setup>
// ✅ Cleanup automatico
watch(query, async (q) => {
  const controller = new AbortController();
  onWatcherCleanup(() => controller.abort());

  await fetch(url, { signal: controller.signal });
});
</script>

📊 Performance Benchmark

Operazione Vue 3.4 Vue 3.5 Miglioramento
SSR render 100ms 50ms 2x
Hydration 80ms 40ms 2x
Large list 150ms 120ms 20%
Deep reactivity 200MB 140MB -30%

🔗 Risorse Utili

💡 Conclusioni

Vue 3.5 "Tengen" è un aggiornamento eccellente:

Reactive Props Destructure per codice più pulito ✅ Performance SSR 2x migliorateuseTemplateRef per refs più esplicite ✅ Memory optimization significativa ✅ Developer Experience migliorata

Quando aggiornare:

  • Compatibile con Vue 3.4 (no breaking changes maggiori)
  • Aggiorna subito per beneficiare delle performance
  • SSR projects beneficiano enormemente

Vue continua a evolversi mantenendo semplicità e performance eccellenti!