9月1日, Vue 3.5 正式发布了!
此次要版本不包含重大更改,并且包括内部改进和有用的新功能。我们将在这篇博文中介绍一些亮点 - 有关更改和新功能的完整列表,请参阅 GitHub 上的完整更新日志。
1. Props 解构
在vue3.5 之前,如果将props直接进行解构,是会失去响应式的,解构出来的值实际上是一个常量。如果需要监听变化,则需要配合toRefs
一起使用。
之前的props声明方式:
const props = withDefaults(
defineProps<{
count?: number
msg?: string
}>(),
{
count: 0,
msg: 'hello'
}
)
vue3.5现在可以简化声明:解构的同时还会保持其响应式。
const { count = 0, msg = 'hello' } = defineProps<{
count?: number
message?: string
}>()
在 3.5 及以上版本中,当同一个 <script setup>
块中的代码访问从 defineProps 解构的变量时(例如 count),Vue 的编译器会自动的访问编译为 props.count
。
使用watch监听解构后的props时,如果按以下这样写,是将一个值而不是响应式数据源传递给 watch。事实上,Vue 的编译器会捕获这种情况并抛出警告。
const { foo } = defineProps(['foo'])
watch(foo, /* ... */)
监听解构后props的正确写法
watch(() => foo, /* ... */)
2. 元素ref的定义
在 3.5 之前,我们可以使用变量名称与 静态ref 属性匹配的普通 refs
。旧方法要求 ref 属性可由编译器分析,因此仅限于静态 ref 属性。
<script setup>
const inputRef = ref('input')
</script>
<template>
<input ref="input">
</template>
相比之下,useTemplateRef()
通过运行时字符串匹配 refs,因此支持动态 ref 的使用场景。
<script setup>
import { useTemplateRef } from 'vue'
const inputRef = useTemplateRef('input')
</script>
<template>
<input ref="input">
</template>
3. Teleport 支持延迟
当使用 Teleport 组件时,指定的目标元素(即 to 属性所指向的选择器对应的元素)必须在页面的 DOM(文档对象模型)结构中已经存在,否则 Teleport 就无法将内容传送过去。
比如,如果你使用 <teleport to="#my-target">
,但页面中没有一个具有 id=“my-target” 的元素,Teleport 就无法正常工作,因为找不到传送内容的目标位置。
但vue3.5中可以通过defer
来延迟Teleport 的挂载,它会等到同一更新周期中的所有其他 DOM 内容都渲染完毕后,再定位目标容器并挂载其子容器。
<Teleport defer to="#target">...</Teleport>
<div id="target"></div>
下面这样写就会找不到目标元素,因为在组件渲染的顺序上,<Teleport>
组件会先尝试查找目标位置,而此时 <div id="target"></div>
还没有被渲染出来。
<Teleport to="#target">...</Teleport>
<div id="target"></div>
4. watch清理回调
有时我们可能会在 watcher 中执行副作用,例如异步请求
watch(id, (newId) => {
fetch(`/api/${newId}`).then(() => {
// 回调事件
})
})
但是,如果 id 在请求完成之前发生更改,该怎么办?当上一个请求完成时,它仍将触发 ID 值已过时的回调。理想情况下,我们希望能够在 id 更改为新值时取消过时的请求。
我们可以使用onWatcherCleanup()
来注册一个清理函数,该函数将在观察程序失效并即将重新运行时调用:
import { watch, onWatcherCleanup } from 'vue'
watch(id, (newId) => {
const controller = new AbortController()
fetch(`/api/${newId}`, { signal: controller.signal }).then(() => {
// 回调事件
})
onWatcherCleanup(() => {
// 如果 id 在请求完成之前发生更改,会取消之前的请求
controller.abort()
})
})
注意:onWatcherCleanup 仅在 Vue 3.5+ 中受支持,并且必须在同步执行 watchEffect 函数或 watch 回调函数期间调用:不能在异步函数中的 await 语句之后调用它。
vue3.5之前中也可以使用onCleanup
函数作为第 3 个参数传递给watch函数:
watch(id, (newId, oldId, onCleanup) => {
// ...
onCleanup(() => {
// 清除逻辑
})
})
watchEffect((onCleanup) => {
// ...
onCleanup(() => {
// 清除逻辑
})
})
还有一些SSR 和自定义元素相关的改进,文中没有提到,有关 3.5 中更改和功能的完整列表,请查看 GitHub 上的完整更改日志