Vue 3.5发布

今天,我们非常兴奋地宣布发布 Vue 3.5 “Tengen Toppa Gurren Lagann”!

这是一个小版本更新,不包含破坏性变更,同时包含内部改进和有用的新功能。我们将在这篇博客文章中介绍一些亮点——完整的更改和新功能列表请参阅 GitHub 上的完整更新日志。

响应式系统优化

在 3.5 版本中,Vue 的响应式系统进行了又一次重大重构,在没有行为变更的情况下,性能得到了提升,内存使用显著改善(减少了 56%)。此重构还解决了 SSR 中由于悬挂计算属性导致的过时计算值和内存问题。

此外,3.5 版本还针对大型、深度响应式数组优化了反应式跟踪,在某些情况下,使这些操作的速度提高了 10 倍。

详细信息:**PR#10397PR#9511**

响应式属性解构

响应式属性解构在 3.5 版本中已被稳定下来。该功能现在默认启用,通过 <script setup> 中的 defineProps 调用解构的变量现在是响应式的。尤其值得注意的是,通过利用 JavaScript 的原生默认值语法,该功能大大简化了具有默认值的属性声明:

之前

1
2
3
4
5
6
7
8
9
10
const props = withDefaults(
defineProps<{
count?: number
msg?: string
}>(),
{
count: 0,
msg: 'hello'
}
)

之后

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

通过编译器,访问解构的变量(如 count)会自动编译为 props.count,因此它们会在访问时被跟踪。类似于 props.count,如果要监视解构后的属性变量或将其传递给组合函数以保持响应性,则需要将其包装在 getter 中:

1
2
3
4
5
6
7
8
watch(count /* ... */)
// ^ 编译时会出现错误

watch(() => count /* ... */)
// ^ 包装在 getter 中,正常工作

// 组合函数应使用 `toValue()` 来标准化输入
useDynamicCount(() => count)

对于那些希望更好地区分解构属性和普通变量的人,@vue/language-tools 2.1 版本提供了一个可选设置,可以为它们启用内联提示:

解构属性的内联提示

详细信息:

  • 查看使用和注意事项的文档
  • 参阅 RFC#502,了解该功能的历史和设计原理。

SSR(服务端渲染)改进

3.5 带来了几项长期请求的服务端渲染改进。

懒加载 Hydration

异步组件现在可以通过 defineAsyncComponent() API 的 hydrate 选项指定何时进行 Hydration。例如,只在组件可见时进行 Hydration:

1
2
3
4
5
6
import { defineAsyncComponent, hydrateOnVisible } from 'vue'

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

核心 API 刻意保持低级别,Nuxt 团队已经在此功能之上构建了更高级别的语法糖。

详细信息:**PR#11458**

useId()

useId() 是一个 API,用于生成在服务器和客户端渲染之间保证稳定的唯一应用程序 ID。这些 ID 可以用于表单元素和可访问性属性,并且在 SSR 应用程序中使用时不会导致 Hydration 不匹配:

1
2
3
4
5
6
7
8
9
10
11
12
<script setup>
import { useId } from 'vue'

const id = useId()
</script>

<template>
<form>
<label :for="id">姓名:</label>
<input :id="id" type="text" />
</form>
</template>

详细信息:**PR#11404**

data-allow-mismatch

在客户端值不可避免地与服务器值不同(例如日期)时,我们现在可以使用 data-allow-mismatch 属性来抑制结果中的 Hydration 不匹配警告:

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

您还可以通过为属性提供值来限制允许的 mismatch 类型,可能的值包括 textchildrenclassstyleattribute

自定义元素改进

3.5 修复了许多与 defineCustomElement() API 相关的长期问题,并为使用 Vue 编写自定义元素添加了许多新功能:

  • 通过 configureApp 选项支持为自定义元素配置应用程序。
  • 添加了 useHost()useShadowRoot()this.$host API,用于访问自定义元素的主机元素和 Shadow Root。
  • 支持通过传递 shadowRoot: false 来挂载没有 Shadow DOM 的自定义元素。
  • 支持提供 nonce 选项,该选项将附加到自定义元素注入的 <style> 标签。

这些新的仅限自定义元素选项可以通过第二个参数传递给 defineCustomElement

1
2
3
4
5
6
7
8
9
import MyElement from './MyElement.ce.vue'

defineCustomElements(MyElement, {
shadowRoot: false,
nonce: 'xxx',
configureApp(app) {
app.config.errorHandler = ...
}
})

其他显著功能

useTemplateRef()

3.5 引入了一种通过 useTemplateRef() API 获取 Template Refs 的新方法:

1
2
3
4
5
6
7
8
9
<script setup>
import { useTemplateRef } from 'vue'

const inputRef = useTemplateRef('input')
</script>

<template>
<input ref="input">
</template>

在 3.5 之前,我们推荐使用变量名与静态 ref 属性匹配的普通 refs。旧方法要求 ref 属性能够被编译器分析,因此仅限于静态 ref 属性。相比之下,useTemplateRef() 通过运行时字符串 ID 匹配 refs,因此支持动态 ref 绑定到变化的 ID。

@vue/language-tools 2.1 也为新语法实现了特殊支持,因此在使用 useTemplateRef() 时,您会根据模板中 ref 属性的存在获得自动完成和警告:

解构属性的内联提示

延迟传送(Deferred Teleport)

内置 <Teleport> 组件的已知限制是其目标元素必须在传送组件挂载时存在。这阻止了用户将内容传送到 Vue 渲染的其他元素中。

在 3.5 中,我们引入了 <Teleport>defer 属性,它在当前渲染周期后挂载,因此现在可以这样使用:

1
2
<Teleport defer target="#container">...</Teleport>
<div id="container"></div>

这种行为需要 defer 属性,因为默认行为需要向后兼容。

详细信息:**PR#11387**

onWatcherCleanup()

3.5 引入了一个全局导入的 API onWatcherCleanup(),用于在观察者中注册清理回调:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { watch, onWatcherCleanup } from 'vue'

watch(id, (newId) => {
const controller = new AbortController()

fetch(`/api/${newId}`, { signal: controller.signal }).then(() => {
// 回调逻辑
})

onWatcherCleanup(() => {
// 终止过时的请求
controller.abort()
})
})

相关:新文档部分关于副作用清理

原文:https://blog.vuejs.org/posts/vue-3-5