Vue 3.5 是 Vue.js 新发布的版本,虽然没有引入重大变更,但带来了许多实用的增强功能、内部优化和性能改进。
1. 响应式系统优化
Vue 3.5 进一步优化了响应式系统的性能,并且减少内存占用。尤其在处理大型或深度嵌套的响应式数组时,性能提高了 10 倍。
举个 🌰
html
<template>
<div>
<p v-for="item in deepArray" :key="item.value">{{ item.value }}</p>
</div>
</template>
<script setup>
import { reactive } from 'vue'
// 创建一个大型的深度嵌套数组,测试响应式系统的优化效果
const deepArray = reactive([...Array(10000)].map(() => reactive({ value: Math.random() })))
</script>
在这个示例中,我们创建了一个包含 10000 个深度嵌套对象的数组。Vue 3.5 对这种场景的内存和性能进行了优化,可以在大量操作和变化时观察到更流畅的响应式处理。
2. 响应式 Props 解构
在 Vue 3.5 中,响应式 props 解构默认启用。在使用 defineProps 时,可以像处理普通 JavaScript 对象那样解构 props,并且解构后的变量保持响应式。
之前的方式:
javascript
const props = withDefaults(
defineProps<{ count?: number; msg?: string }>(),
{ count: 0, msg: 'hello' }
)
新方式:
javascript
const { count = 0, msg = 'hello' } = defineProps<{ count?: number; msg?: string }>()
解构后的变量 count 和 msg 在模板中使用时会自动保持响应式,且不需要显式地调用 toRefs。
但我没有成功在 Vue 3.5 的项目下运行成功,还需要研究一下 o(╥﹏╥)o。
3. 服务器渲染(SSR)改进
3.1 懒加载补水(Lazy Hydration)
Vue 3.5 引入了懒加载补水功能,使用 defineAsyncComponent() 可以控制异步组件的水合时机。例如:允许异步组件在首次可见时才进行补水操作,减少初次渲染的资源消耗。
3.1.1解释一下补水(水合操作)!!
补水(Hydration)是一个与**服务器端渲染(SSR)相关的术语。**
****在 SSR 中,Vue 会在服务器上预先渲染组件的 HTML,并将其发送到浏览器。当页面加载时,客户端的 JavaScript 代码会接管 这些已经存在的 HTML 元素,并将它们变为动态响应式 ,这个过程就被称为水合操作(Hydration)。
补水(水合操作)的意义
水合操作的主要目的是将服务器端预渲染的静态 HTML 与客户端的动态 JavaScript 逻辑 连接起来,使页面在首次加载时可以快速显示内容,同时在客户端加载完 JavaScript 之后,页面可以正常交互。这种做法提高了页面的首次加载速度 和用户体验,特别是在网络环境较差或页面较为复杂时。
补水与懒加载补水
1、普通补水
页面加载时,所有的静态 HTML 会立即被客户端 JavaScript "接管"。这种做法可能会导致在页面初次渲染时,客户端需要同时处理大量的水合任务,从而影响性能。
2、懒加载补水
Vue 3.5 引入的懒加载补水功能指,只在组件首次出现在视口中时才进行水合操作。这样可以减少页面初次渲染时的性能开销,只在需要时才补水,特别适合异步加载的组件或在页面滚动到可见区域时才需要的内容。
举个 🌰
假设我们有一个异步加载的组件 AsyncComponent.vue,在普通补水情况下,这个组件在 SSR 中生成的 HTML 会在页面加载时立即被水合,无论用户是否滚动到该组件的可见区域。而在懒加载补水的情况下,只有当用户滚动到组件可见时,才会触发水合。
App.vue
html
<template>
<div class="container">
<AsyncComp v-if="visible" />
<button @click="toggleVisibility">Toggle Component</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { defineAsyncComponent, hydrateOnVisible } from 'vue'
const visible = ref(false)
const toggleVisibility = () => {
visible.value = !visible.value
}
const AsyncComp = defineAsyncComponent({
loader: () => import('./components/AsyncComponent.vue'), // 模拟异步加载的组件
hydrate: hydrateOnVisible() // 只有在组件可见时才进行水合
})
</script>
AsyncComponent.vue
html
<template>
<div>
异步加载的组件
</div>
</template>
展示为:
3.2 useId( ) API
useId( ) 是一个新 API,专门为生成在服务器和客户端渲染过程中保持稳定的唯一 ID。这对于生成表单元素和可访问性属性的 ID 非常有用。
举个 🌰
html
<template>
<form>
<label :for="id">Name:</label>
<input :id="id" type="text" />
</form>
</template>
<script setup>
import { useId } from 'vue'
// 使用 useId 生成一个唯一的 id
const id = useId()
console.log("~ id:", id)
</script>
此功能确保生成的 ID 在 SSR 和客户端渲染时保持一致,确保表单和可访问性属性不会因不匹配导致警告或错误。
4. 自定义元素改进
Vue 3.5 对 defineCustomElement() API 进行了增强,使 Vue 创建 Web Components 更加灵活。
举个 🌰 App.vue
html
<template>
<div class="container">
<!-- Vue 自定义元素可以像普通 HTML 元素一样使用 -->
<my-element title="使用自定义元素" description="这是通过 Vue 定义的 Web Component。"></my-element>
<br />
<my-element />
</div>
</template>
<script setup>
import { defineCustomElement } from 'vue'
import MyElement from './components/MyElement.ce.vue'
// 使用 defineCustomElement 注册组件
const MyElementCustom = defineCustomElement(MyElement, {
shadowRoot: false,
})
// 通过 customElements.define 注册为自定义元素
customElements.define('my-element', MyElementCustom)
</script>
MyElement.ce.vue
html
<template>
<div>
<h1>{{ title }}</h1>
<p>{{ description }}</p>
</div>
</template>
<script>
export default {
props: {
title: {
type: String,
default: '默认标题'
},
description: {
type: String,
default: '这是自定义元素的描述内容'
}
}
}
</script>
展示为:
4.1 解释一下 .ce.vue
在 Vue.js 中,.ce.vue 文件名 中的**.ce** 通常表示自定义元素(Custom Element) 。这种命名方式并不是 Vue 官方强制要求的,而是一种约定俗成的命名规范 ,用于区分普通的 Vue 组件和用来创建Web Components的自定义元素组件。
自定义元素在许多场景中很有用,特别是希望在多个框架之间共享组件,或者希望组件能够独立运行时,Web Components 是一个很好的选择。而 .ce.vue 文件命名则帮助开发者清楚地知道这个 Vue 组件是为生成 Web Components 而设计的。
5. 新增 useTemplateRef( )
Vue 3.5 引入了 useTemplateRef() API,它允许动态地获取模板引用,特别适用于引用 ID 动态变化或条件变化的场景。相较于传统的 ref,useTemplateRef 可以在运行时根据不同的条件动态更新引用,而不是依赖于静态的 ref 属性。
举个 🌰
html
<template>
<input ref="inputRef" type="text" />
</template>
<script setup>
import { useTemplateRef } from 'vue'
// 获取动态引用的 input
const inputRef = useTemplateRef('inputRef')
// 在生命周期中可以访问这个引用
onMounted(() => {
inputRef.value.focus()
})
</script>
6. 延迟传送(defer Teleport)
Vue 内置 <Teleport> 组件在传送内容时,要求目标元素在组件挂载时已经存在。Vue 3.5 引入了 defer 属性,许传送内容到后才渲染的目标元素。
举个 🌰
html
<template>
<div class="container">
<Teleport defer to="#dynamic-target">
<p>传送内容...</p>
</Teleport>
<div id="dynamic-target"></div>
</div>
</template>
<script setup>
import { onMounted } from 'vue'
onMounted(() => {
setTimeout(() => {
// 模拟目标元素动态渲染
document.getElementById('dynamic-target').innerHTML = '<div>目标元素已渲染</div>'
}, 1000)
})
</script>
展示为:
这里 Teleport 组件会等待目标元素(#dynamic-target)渲染后再将内容传送过去,可以解决传送目标先于组件渲染的问题。
7. 新增 onWatcherCleanUp()
Vue 3.5 引入了 onWatcherCleanup() API,用于在清理 watch 时注册回调函数。例如,可以在 watch 的回调中清理过时的网络请求。
举个 🌰
html
<template>
<div>
<button @click="id++">更改 ID</button>
<p>当前 ID: {{ id }}</p>
</div>
</template>
<script setup>
import { ref, watch, onWatcherCleanup } from 'vue';
const id = ref(1);
// 监控 id 的变化,并在 watcher 停止时清理过时的网络请求
watch(id, (newId) => {
const controller = new AbortController();
// 发起网络请求
fetch(`/api/data/${newId}`, { signal: controller.signal })
.then((response) => {
if (!response.ok) {
throw new Error(`网络请求失败: ${response.status}`);
}
return response.json(); // 解析 JSON
})
.then((data) => {
console.log('获取的数据:', data);
})
.catch((error) => {
if (error.name === 'AbortError') {
console.log('请求被取消');
} else {
console.error('发生错误:', error);
}
});
// 注册清理函数,取消旧的请求
onWatcherCleanup(() => {
controller.abort();
});
});
</script>
此功能允许在 watch 停止追踪时自动执行清理操作,避免资源泄漏。
8. 总结
Vue 3.5 版本提供了多项增强功能,包括响应式系统的性能优化、响应式 props 解构、SSR 改进、自定义元素支持的扩展等,优化内存、提升性能的同时也提升了 Vue 的开发体验。
有些内容可能不是很详细,大家感兴趣的话,可以自行研究一下。
在此之前:需要确保项目使用的是 Vue 3.5。可以使用以下命令更新项目中的 Vue 版本:
bash
npm install vue@latest
# or
yarn add vue@latest
然后,检查项目中的 package.json,确认 Vue 版本已经更新到 3.5 或更高: