Vue3.5 发布已近半年,抽空整理下常用的新增/改动特性
响应式 Props 解构
Vue3.5 中 Props 正式支持解构了,并添加了响应式跟踪
设置默认值
使用 JavaScript 原生的默认值语法声明 props 默认值
以前
|---|-------------------------------|
| | const props = withDefaults(
|
| | defineProps<{
|
| | count?: number
|
| | msg?: string
|
| | }>(),
|
| | {
|
| | count: 0,
|
| | msg: 'hello'
|
| | }
|
| | )
|
现在
|---|-------------------------------------------------------|
| | const { count = 0, msg = 'hello' } = defineProps<{
|
| | count?: number
|
| | message?: string
|
| | }>()
|
响应式解构
当在同一个 <script setup>
代码块中访问由 defineProps
解构的变量时,Vue 编译器会自动在前面添加 props
以前
|---|----------------------------------------|
| | const { foo } = defineProps(['foo'])
|
| | |
| | watchEffect(() => {
|
| | // 在 3.5 之前只运行一次
|
| | console.log(foo)
|
| | })
|
现在
|---|-----------------------------------------------------------------|
| | const { foo } = defineProps(['foo'])
|
| | |
| | watchEffect(() => {
|
| | // 在 3.5 中在 "foo" prop 变化时重新执行
|
| | console.log(foo)
|
| | ``// foo
由编译器转换为 props.foo
,以上等同于 console.log(props.foo)``` | | |
})` |
与之类似,监听解构的 prop 变量 或 将其传递到可组合项中同时保留响应性 时需要将其包装在 getter 中
以前
|---|----------------------------------------|
| | const { foo } = defineProps(['foo'])
|
| | |
| | watch(foo, /* ... */)
|
现在:
|---|---------------------------------------------------------------------------------|
| | // watch(foo, /* ... */) 等价于 watch(props.foo, ...),我们给 watch 传递的是一个值而不是响应式数据源
|
| | watch(() => foo, /* ... */)
|
| | |
| | // 传递解构的 prop 到外部函数中并保持响应性
|
| | useComposable(() => foo)
|
监听(watch
/ watcheffect
) 相关
watch 支持指定深度 deep: number
watch 的 deep
选项现在支持传入数字,来指定监听的深度
|---|------------------------------------------|
| | const state = ref({
|
| | a: {
|
| | b: {
|
| | c: 1
|
| | }
|
| | }
|
| | })
|
| | |
| | watch(state, (newValue) => {
|
| | console.log(`state: ${newValue}`)
|
| | },
|
| | { deep: 2 }
|
| | )
|
| | |
| | state.a.b = { c: 2 } // 更改了第二层的属性,触发监听
|
| | state.a.b.c = 2 // 更改了第三层的属性,不触发监听
|
清理函数 onWatcherCleanup / onEffectCleanup
以前我们在监听函数中要发送异步请求时,很可能发生请求参数发生变化的情况,这时我们需要设置全局变量存储 AbortController
,并在组件卸载之前清理它
|---|----------------------------------------------------------------------------|
| | import { watch, onBeforeUnmount } from "vue"
|
| | |
| | let controller = new AbortController()
|
| | |
| | watch(state, (newValue) => {
|
| | controller.abort() // 取消上一次的请求
|
| | controller = new AbortController()
|
| | |
| | fetch(`/api/${newValue}`, { signal: controller.signal }).then(() => {
|
| | // 回调逻辑
|
| | })
|
| | });
|
| | |
| | // 组件卸载前也要清理
|
| | onBeforeUnmount(() => controller.abort())
|
现在有了清理函数 onWatcherCleanup
/ onEffectCleanup
后,我们可以直接调用它来清理之前的 调用(异步)函数/请求
|---|-------------------------------------------------------------------------|
| | import { watch, onWatcherCleanup } from 'vue'
|
| | |
| | watch(id, (newId) => {
|
| | const controller = new AbortController()
|
| | |
| | fetch(`/api/${newId}`, { signal: controller.signal }).then(() => {
|
| | // 回调逻辑
|
| | })
|
| | |
| | onWatcherCleanup(() => {
|
| | // 终止过期请求
|
| | controller.abort()
|
| | })
|
| | })
|
onEffectCleanup
函数写法类似以上,不同的是导入来源
|---|------------------------------------------------------|
| | import { onEffectCleanup } from "@vue/reactivity";
|
[!WARNING]
onWatcherCleanup
仅在 Vue 3.5+ 中支持,并且必须在watchEffect
效果函数或watch
回调函数的同步执行期间调用:你不能在异步函数的await
语句之后调用它。
watch 返回值增强
watch 返回值中新增 暂停/恢复侦听器,可以更细致的控制监听作用范围
|---|---------------------------------------------------|
| | const { stop, pause, resume } = watch(() => {})
|
| | |
| | // 暂停侦听器
|
| | pause()
|
| | |
| | // 稍后恢复
|
| | resume()
|
SSR 改进
惰性激活 Lazy Hydration
异步组件可以通过 defineAsyncComponent() API
中的 hydrate
选项来控制何时进行激活
在空闲时进行激活
|---|-------------------------------------------------------------|
| | import { defineAsyncComponent, hydrateOnIdle } from 'vue'
|
| | |
| | const AsyncComp = defineAsyncComponent({
|
| | loader: () => import('./Comp.vue'),
|
| | hydrate: hydrateOnIdle(/* 传递可选的最大超时 */)
|
| | })
|
在元素变为可见时激活
|---|----------------------------------------------------------------|
| | import { defineAsyncComponent, hydrateOnVisible } from 'vue'
|
| | |
| | const AsyncComp = defineAsyncComponent({
|
| | loader: () => import('./Comp.vue'),
|
| | hydrate: hydrateOnVisible()
|
| | })
|
自定义策略
|---|-------------------------------------------------------------------------|
| | import { defineAsyncComponent, type HydrationStrategy } from 'vue'
|
| | |
| | const myStrategy: HydrationStrategy = (hydrate, forEachElement) => {
|
| | // forEachElement 是一个遍历组件未激活的 DOM 中所有根元素的辅助函数,
|
| | // 因为根元素可能是一个片段而非单个元素
|
| | forEachElement(el => {
|
| | // ...
|
| | })
|
| | ``// 准备好时调用 hydrate``` | | |
hydrate() | | |
return () => { | | |
// 如必要,返回一个销毁函数 | | |
} | | |
} | | | | | |
const AsyncComp = defineAsyncComponent({ | | |
loader: () => import('./Comp.vue'), | | |
hydrate: myStrategy | | |
})` |
其他
请查看 Vue3官方文档 - 惰性激活,这里不再赘述
useId()
生成唯一应用ID
用于为无障碍属性或表单元素生成每个应用内唯一的 ID。在我们日常应用中,主要可以解决服务端和客户端生成的id
不一样导致渲染报错的问题
|---|----------------------------------|
| | <script setup>
|
| | import { useId } from 'vue'
|
| | |
| | const id = useId()
|
| | </script>
|
| | |
| | <template>
|
| | <form>
|
| | <label :for="id">Name:</label>
|
| | <input :id="id" type="text" />
|
| | </form>
|
| | </template>
|
data-allow-mismatch
如果客户端值不可避免地与其服务端对应值(例如日期)不同,我们可以使用属性 data-allow-mismatch
来避免由此产生的激活不匹配警告
|---|-----------------------------------------------------------------|
| | <span data-allow-mismatch>{ { data.toLocaleString() }}</span>
|
还可以指定特定类型。允许的值有:text
,children
(仅允许直接子组件不匹配),class
,style
,attribute
其他
useTemplateRef()
返回一个浅层 ref,可以更直观的绑定元素,同时也支持动态绑定
|---|----------------------------------------------------------------------|
| | <script setup>
|
| | import { ref, useTemplateRef, onMounted } from 'vue'
|
| | |
| | const targetRef = ref('input1')
|
| | const inputRef = useTemplateRef<HTMLInputElement>(targetRef.value)
|
| | |
| | onMounted(() => {
|
| | inputRef.value.focus()
|
| | })
|
| | </script>
|
| | |
| | <template>
|
| | <input ref="input1" />
|
| | <input ref="input2" />
|
| | </template>
|
其他不常用的就不在说明了
![](https://i-blog.csdnimg.cn/direct/bfdd33269aee43ee93b0fb3fc31c4f07.jpeg)
关注灵活就业新业态,关注公账号:贤才宝(贤才宝https://www.51xcbw.com)