摘要
文章对 Vue3.5 版本的新特性做了全面解读,包括响应式相关改进、SSR 服务端渲染的更新、组件相关改进等,这些新特性在特定场景下有很大用处,值得开发者升级。
一、响应式相关改进
响应式相关改进包含多个方面内容。
1. 重构响应式
这是 Vue 内部优化,普通开发者无感。其通过版本计数和双向链表数据结构(灵感来源于 Preact signals)使内存占用减少 56%。作者后续还会出响应式相关源码文章。
2. 响应式 props 支持解构
在 3.5 版本之前,在 js 中访问 prop 必须写props.name
,否则name
会丢失响应式。3.5 版本有了响应式 props 解构后,可以直接解构出name
使用,例如:
<script setup lang="ts">
const { name } = defineProps({
name: String,
});
console.log(name);
</script>
编译后简化代码如下:
setup(__props) {
console.log(__props.name);
const __returned__ = {};
return __returned__;
}
这样name
就不会丢失响应式。
3. 新增 onEffectCleanup 函数
在组件卸载之前或者下一次watchEffect
回调执行之前会自动调用onEffectCleanup
函数。例如:
import { watchEffect, ref } from "vue";
import { onEffectCleanup } from "@vue/reactivity";
const flag = ref(true);
watchEffect(() => {
if (flag.value) {
const timer = setInterval(() => {
// 做一些事情
console.log("do something");
}, 200);
onEffectCleanup(() => {
clearInterval(timer);
});
}
});
有了这个函数就不需要在beforeUnmount
钩子函数中清理timer
。不过要注意这个函数目前没有在vue
包中暴露,需要从@vue/reactivity
包中导入。
4. 新增 base watch 函数
之前watch
函数和 Vue 组件及生命周期深度绑定,代码在runtime - core
模块。但在一些场景(如小程序vuemini
)只想使用vue
的响应式功能(reactivity
模块)时,3.5 版本重构了base watch
函数,它在reactivity
模块,和 Vue 组件无关系。这对普通开发者影响不大,但对下游项目(如vuemini
)有益。
5. 新增 onWatcherCleanup 函数
与onEffectCleanup
函数类似,在组件卸载之前或者下一次watch
回调执行之前会自动调用onWatcherCleanup
函数。例如:
import { watch, ref, onWatcherCleanup } from "vue";
watch(flag, () => {
const timer = setInterval(() => {
// 做一些事情
console.log("do something");
}, 200);
onWatcherCleanup(() => {
console.log("清理定时器");
clearInterval(timer);
});
});
这里可以直接从vue
中导入onWatcherCleanup
函数。
6. 新增 pause 和 resume 方法
在一些场景中想暂停watch
或者watchEffect
中的回调,满足业务条件后再恢复执行就可以使用这两个方法。例如watchEffect
的例子:
<template>
<button @click="count ++">count ++</button>
<button @click="runner2.pause()">暂停</button>
<button @click="runner2.resume()">恢复</button>
</template>
<script setup lang="ts">
import { watchEffect } from "vue";
const count = ref(0);
const runner2 = watchEffect(() => {
if (count.value > 0) {
console.log(count.value);
}
});
</script>
点击count ++
按钮理论上每次都会执行watchEffect
回调,点击暂停按钮执行pause
方法暂停后回调就不执行了,点击恢复按钮执行resume
方法恢复后回调重新执行。watch
也可以执行这两个方法。
7. watch 的 deep 选项支持传入数字
以前deep
选项的值只能是false
或true
,表示是否深度监听对象。3.5 版本deep
选项支持传入数字,表示监控对象的深度。例如:
const obj1 = ref({
a: {
b: 1,
c: {
d: 2,
e: {
f: 3,
},
},
},
});
watch(
obj1,
() => {
console.log("监听到 obj1 变化");
},
{
deep: 3,
}
);
function changeDeep3Obj() {
obj1.value.a.c.d = 20;
}
function changeDeep4Obj() {
obj1.value.a.c.e.f = 30;
}
deep
选项值为 3 时,修改对象第 3 层的d
属性能触发watch
回调,修改第 4 层的f
属性不能触发。
二、SSR 服务端渲染
1. 新增 useId 函数
在 SSR 服务端渲染中,如果像下面这样生成随机数作为id
会有警告:
<template>
<label :htmlFor="id">Do you like Vue3.5?</label>
<input type="checkbox" name="vue3.5" :id="id" />
</template>
<script setup lang="ts">
const id = Math.random();
</script>
因为服务端和客户端都会执行Math.random()
生成不同的id
。useId
函数可解决此问题,它也可用于客户端渲染中需要唯一键但服务端未提供的场景。
2. Lazy Hydration 懒加载水合
异步组件现在可通过defineAsyncComponent()
API 的hydrate
选项控制何时水合,作者认为普通开发者用不上所以未细讲。
3. data - allow - mismatch
在 SSR 中,服务端和客户端生成的html
可能不一致(如渲染当前时间)会有警告,可使用data - allow - mismatch
属性干掉警告,例如:
<template>
<div data - allow - mismatch>当前时间是:{{ new Date() }}</div>
</template>
三、组件相关改进
1. Teleport 组件新增 defer 延迟属性
Teleport
组件可将children
内容传送到指定位置。之前有个限制,不能将目标元素放在Teleport
组件后面。3.5 版本新增defer
延迟属性解决了此问题。例如:
<Teleport defer to="# target">被传送的内容</Teleport>
<div id="target"></div>
defer
延迟属性等一轮渲染周期结束后再渲染Teleport
组件,所以就算目标元素在Teleport
组件后面也能正常传送内容。
2. useTemplateRef 函数
在vue3
中使用ref
访问 DOM 和子组件存在一些问题,比如定义的ref
变量是响应式数据还是 DOM 元素容易混淆,template
中ref
属性值是字符串却能和script
中同名变量绑定等。3.5 版本的useTemplateRef
函数可解决这些问题。例如:
<input type="text" ref="inputRef" />
const inputEl = useTemplateRef<HTMLInputElement>("inputRef");
使用useTemplateRef
函数后返回的ref
变量指向 DOM 元素输入框,比之前的体验好很多。
四、总结
对于开发者来说,Vue3.5 版本新增了很多有趣功能,如onEffectCleanup
函数、onWatcherCleanup
函数、pause
和resume
方法、watch
的deep
选项支持传入数字、useId
函数、Teleport
组件新增defer
延迟属性、useTemplateRef
函数等。这些功能在特殊场景下很有用,作者认为值得将 Vue 升级到 3.5 版本。