@[TOC](Vue 3.5更新)
前言
今天,我们很高兴地宣布 Vue 3.5 “Tengen Toppa Gurren Lagann” 的发布!
此次要版本不包含重大更改,并且包括内部改进和有用的新功能。我们将在这篇博文中介绍一些亮点 - 有关更改和新功能的完整列表,请参阅 GitHub 上的完整更新日志。
响应式系统优化
在 3.5 中,Vue 的响应性系统经历了另一次重大重构,实现了更好的性能并显着提高了内存使用率 (-56%),而行为没有变化。此重构还解决了在 SSR 期间因挂起计算而导致的过时计算值和内存问题。
此外,3.5 还优化了大型深度响应式数组的响应性跟踪,在某些情况下使此类操作的速度提高了 10 倍。
响应式的 Props 解构
Reactive Props Destructure 在 3.5 中得到了稳定。现在,默认情况下启用该功能后,从
Before 以前
const props = withDefaults(
defineProps<{
count?: number
msg?: string
}>(),
{
count: 0,
msg: 'hello'
}
)
After 后
const { count = 0, msg = 'hello' } = defineProps<{
count?: number
message?: string
}>()
编译器会自动将对解构变量(例如 count)的访问编译到 props.count 中,因此在访问时会跟踪它们。与 props.count 类似,观察解构的 prop 变量或将其传递到可组合项中同时保留响应性需要将其包装在 getter 中:
watch(count /* ... */)
// ^ results in compile-time error
watch(() => count /* ... */)
// ^ wrap in a getter, works as expected
// composables should normalize the input with `toValue()`
useDynamicCount(() => count)
对于那些喜欢更好地区分解构 props 和普通变量的人,@vue/language-tools 2.1 提供了一个 opt-in 设置来为它们启用 inlay 提示
详情:
SSR 改进
3.5 为服务器端渲染 (SSR) 带来了一些期待已久的改进。
Lazy Hydration
异步组件现在可以控制何时应该冻结它们,方法是通过 defineAsyncComponent() API 的 hydrate 选项指定策略。例如,要仅在组件变得可见时水合组件:
import { defineAsyncComponent, hydrateOnVisible } from 'vue'
const AsyncComp = defineAsyncComponent({
loader: () => import('./Comp.vue'),
hydrate: hydrateOnVisible()
})
核心 API 有意降低级别,Nuxt 团队已经在此功能之上构建了更高级别的语法糖。
详情: PR#11458
useId()
useId() 是一个 API,可用于生成每个应用程序唯一的 ID,这些 ID 保证在服务器和客户端渲染中保持稳定。它们可用于生成表单元素和辅助功能属性的 ID,并且可以在 SSR 应用程序中使用,而不会导致激活不匹配:
<script setup>
import { useId } from 'vue'
const id = useId()
</script>
<template>
<form>
<label :for="id">Name:</label>
<input :id="id" type="text" />
</form>
</template>
详细: PR#11404
data-allow-mismatch
如果客户端值不可避免地与其服务器对应值(例如日期)不同,我们现在可以使用 data-allow-mismatch 属性抑制生成的水合不匹配警告:
<span data-allow-mismatch>{{ data.toLocaleString() }}</span>
您还可以通过为属性提供值来限制允许的不匹配类型,其中可能的值为 text、children、class、style 和 attribute。
Custom Elements 改进
3.5 修复了许多与 defineCustomElement() API 相关的长期问题,并添加了许多使用 Vue 编写自定义元素的新功能:
- 通过 configureApp 选项支持自定义元素的应用程序配置。
- 新增 useHost()、useShadowRoot() 和 this.$host API,用于访问自定义元素的 host element 和 shadow root。
- 支持通过传递 shadowRoot: false 来挂载没有 Shadow DOM 的自定义元素。
- 支持提供 nonce 选项,该选项将附加到自定义元素注入的 标签上。
这些新的仅限自定义元素的选项可以通过第二个参数传递给 defineCustomElement:
import MyElement from './MyElement.ce.vue'
defineCustomElements(MyElement, {
shadowRoot: false,
nonce: 'xxx',
configureApp(app) {
app.config.errorHandler = ...
}
})
其他显着特点
useTemplateRef()
3.5 引入了一种通过 useTemplateRef() API 获取模板引用的新方法:
<script setup>
import { useTemplateRef } from 'vue'
const inputRef = useTemplateRef('input')
</script>
<template>
<input ref="input">
</template>
在 3.5 之前,我们建议使用变量名称与 static ref 属性匹配的普通 ref。旧方法要求 ref 属性可由编译器分析,因此仅限于静态 ref 属性。相比之下,useTemplateRef() 通过运行时字符串 ID 匹配 refs,因此支持动态 ref 绑定到不断变化的 ID。
@vue/language-tools 2.1 还实现了对新语法的特殊支持,因此在使用 useTemplateRef() 时,你会根据模板中是否存在 ref 属性而收到自动补全和警告:
Deferred Teleport
内置 组件的一个已知约束是,在挂载 Teleport 组件时,其目标元素必须存在。这阻止了用户在传送后将内容传送到 Vue 渲染的其他元素。 在 3.5 中,我们为 引入了一个 defer 属性,它会在当前渲染周期之后挂载它,所以这现在可以工作了:
<Teleport defer target="#container">...</Teleport>
<div id="container"></div>
此行为需要 defer prop,因为默认行为需要向后兼容。
详情:PR#11387
onWatcherCleanup()
3.5 引入了一个全局导入的 API onWatcherCleanup(),用于在 watcher 中注册清理回调:
import { watch, onWatcherCleanup } from 'vue'
watch(id, (newId) => {
const controller = new AbortController()
fetch(`/api/${newId}`, { signal: controller.signal }).then(() => {
// callback logic
})
onWatcherCleanup(() => {
// abort stale request
controller.abort()
})
})
相关新闻: 关于副作用清理的新文档部分
结语
有关 3.5 中更改和功能的完整列表,请查看 GitHub 上的完整更改日志。祝您黑客愉快!