Vue 3.5 has been released — another important version following 3.4. The most significant change is a complete rewrite of the reactivity system, bringing notable memory and performance improvements.
Reactivity System Rewrite
Vue 3.5 refactored the underlying implementation of reactive(), ref(), and computed(), with the core goal of reducing memory usage.
Official data shows:
内存占用降低 56%(大型响应式对象场景)
The rewrite focused on optimizing the Proxy handler implementation and reducing the creation of intermediate objects.
Impact on Real Projects
We have a data-intensive admin panel where pages often mount 1000+ reactive nodes:
<script setup lang="ts">
// 以前用 shallowRef 处理大数组避免响应式开销
const largeList = shallowRef<Item[]>([]);
// Vue 3.5 之后,普通 ref 也足够轻量
const largeList = ref<Item[]>([]);
// 1000 个对象的响应式包装,内存从 ~8MB 降到 ~3.5MB
</script>
effectScope Enhancement
Vue 3.5 enhanced the effectScope API, making unified management of side effects more elegant:
import { effectScope, watch, ref, onScopeDispose } from "vue";
function useDataSync(key: string) {
const scope = effectScope();
scope.run(() => {
const data = ref(null);
// 所有 watch 都在这个 scope 内
watch(
data,
(val) => {
localStorage.setItem(key, JSON.stringify(val));
},
{ deep: true }
);
// scope 销毁时,所有 watch 自动清理
onScopeDispose(() => {
console.log(`sync for ${key} disposed`);
});
});
return scope;
}
// 在组件中使用
const syncScope = useDataSync("user-settings");
// 组件卸载时,scope 内的所有副作用自动清理
defineModel Stabilized
Introduced in Vue 3.4, defineModel becomes a stable API in 3.5, no longer requiring the experimental flag:
<!-- 子组件 -->
<script setup>
// 以前:需要 modelValue + emit update
// const props = defineProps(['modelValue']);
// const emit = defineEmits(['update:modelValue']);
// Vue 3.5:一行搞定
const modelValue = defineModel();
const count = defineModel("count", { default: 0 });
</script>
<template>
<input v-model="modelValue" />
<input v-model="count" type="number" />
</template>
useId
The new useId composable generates SSR-safe unique IDs:
<script setup>
import { useId } from "vue";
const labelId = useId(); // "v-0"
const inputId = useId(); // "v-1"
// SSR 和 CSR 水合时保证一致
</script>
<template>
<label :for="inputId">用户名</label>
<input :id="inputId" :aria-describedby="labelId" />
</template>
Lazy Teleport
The Teleport component gains a defer option, delaying teleportation until the DOM is ready:
<template>
<!-- 以前:如果 #modal-root 还没渲染,Teleport 会报错 -->
<Teleport to="#modal-root" defer>
<Modal />
</Teleport>
</template>
data-allow-mismatch
A new attribute for handling SSR hydration mismatches:
<template>
<!-- 日期、相对时间等经常不一致的内容 -->
<time data-allow-mismatch>{{ formattedDate }}</time>
</template>
Team Upgrade Recommendations
pnpm update vue@3.5 @vitejs/plugin-vue
Upgrade checklist:
- Remove the
experimentalDefineModelconfiguration - In large-array scenarios, remove
shallowRefand use regularref - Migrate component ID generation logic to
useId - Test that SSR hydration works correctly
Summary
- Reactivity system rewrite: memory usage reduced by ~56%, significant gains in large-data scenarios
defineModelstabilized: simplifies v-model two-way bindinguseId: SSR-safe unique ID generationeffectScopeenhanced: unified side-effect management- Lazy Teleport: solves the issue of the target node not being ready