摘要
文章对 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 版本。
