前端人的苦恼叕来了,前端技术隔三岔五的更新,学习别想停了,趁着中秋即将来临卷起来吧(说好的中秋假期咱不卷的呢)。就在这个9月,尤大叕更新了,没事,一文总结重要更新,大概更新了以下内容:
-
响应式重构。性能提升了,内存使用率下降了(56%)
-
响应式 props 解构
-
新增 useTemplateRef 函数
-
服务端渲染SSR主要几个部分:新增useId函数、Lazy Hydration 懒加载水合、data-allow-mismatch
-
新增 onEffectCleanup函数
-
新增 base watch 函数
-
新增 onWatcherCleanup
-
新增 pause和resume方法
-
改进的 Suspense支持
1、响应式props支持解构,在之前,我们从 props 中解构出来的是不具备响应式的,需要具备响应式解构需要用到 toRefs 方法包装 props进行解构,而3.5之后,就无需 toRefs 包装了。
<template>
<div>
child值:
<span style="color: red">{{ data }}</span>
</div>
</template>
<script setup>
import { watchEffect } from "vue";
const { data } = defineProps(["data"]); // 仍具备响应式
watchEffect(() => {
console.log("data", data);
});
2、 useTemplateRef 函数,在之前,我们获取 dom 节点需要给 dom 绑定上一个ref=‘refKey’, 然后再用一个ref定义一个空的响应式属性赋值给这个refKey。如下所示
<template>
<div class="content" ref='myNode'>dom</div>
</template>
<script setup>
import { ref, onMounted } from "vue";
const myNode = ref(null)
onMounted(()=>{
console.log(myNode) // 拿到 dom 节点
})
</script>
而现在,有了 useTemplateRef 函数,我们可以更好的区分响应式属性和 dom 节点,不像之前响应式属性和dom节点都是通过ref定义和获取。
useTemplateRef 用法如下
<template>
<span style="color: red" ref="myNode">是我</span>
</template>
<script setup>
import { useTemplateRef } from "vue";
const node = useTemplateRef("myNode"); // 这里定义的接收dom节点的变量无需和ref一样,随便定义接收
onMounted(()=>{
console.log(node) // 拿到 dom 节点
})
</script>
3,新增onEffectCleanup函数,在组件卸载之前或者下一次watchEffect回调执行之前会自动调用onEffectCleanup函数,有了这个函数后你就不需要在组件的beforeUnmount钩子函数去统一清理一些timer了。比如下面这个场景:
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);
});
}
});
4,新增onWatcherCleanup函数,和前面的onEffectCleanup函数类似,在组件卸载之前或者下一次watch回调执行之前会自动调用onWatcherCleanup函数,同样有了这个函数后你就不需要在组件的beforeUnmount钩子函数去统一清理一些timer了。比如下面这个场景:
import { watch, ref, onWatcherCleanup } from "vue";
watch(flag, () => {
const timer = setInterval(() => {
// 做一些事情
console.log("do something");
}, 200);
onWatcherCleanup(() => {
console.log("清理定时器");
clearInterval(timer);
});
});
和onEffectCleanup函数不同的是我们可以从vue中import导入onWatcherCleanup函数。
5,新增了一个base watch函数,这个函数用法和我们熟知的watch API一模一样。区别就是我们之前用的watch API是和Vue组件以及生命周期是一起实现的,他们是深度绑定的。而Vue3.5新增的base watch函数是一个新的函数,他的实现和Vue组件以及生命周期没有一毛钱关系。
为什么将 watch 重构到 reactivity 模块?
将 watch 函数从 runtime-core 模块重构到 reactivity 模块,是一个深思熟虑的决定。在 Vue 的模块化设计中,reactivity 模块负责响应式系统的实现,而 watch 作为观察响应式数据变化的重要工具,理应与响应式系统紧密集成。
这一重构不仅使得 watch 函数的实现更加合理,也方便了下游项目如 Vue Mini 的使用。在过去,由于 watch 函数位于 runtime-core 模块,下游项目如果需要实现类似功能,往往需要手写或复制粘贴代码。而现在,reactivity 模块直接提供了 watch 函数的实现,大大简化了这些项目的开发工作。
还有一点就是这个base watch函数对于普通开发者来说没有什么影响,但是对于一些下游项目,比如vuemini来说是受益的。
watch的deep选项支持传入数字,在以前deep选项的值要么是false,要么是true,表明是否深度监听一个对象。在3.5中deep选项支持传入数字了,表明监控对象的深度。
比如下面的这个demo:
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;
}
在上面的例子watch的deep选项值是3,表明监听到对象的第3层。changeDeep3Obj函数中就是修改对象的第3层的d属性,所以能够触发watch的回调。而changeDeep4Obj函数是修改对象的第4层的f属性,所以不能触发watch的回调。
6,新增pause和resume方法,有的场景中我们可能想在“一段时间中暂停一下”,不去执行watch或者watchEffect中的回调。等业务条件满足后再去恢复执行watch或者watchEffect中的回调。在这种场景中pause和resume方法就能派上用场啦。
下面这个是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>
在上面的demo中,点击count++按钮后理论上每次都会执行一次watchEffect的回调。
但是当我们点击了暂停按钮后就会执行pause方法进行暂停,在暂停期间watchEffect的回调就不会执行了。
当我们再次点击了恢复按钮后就会执行resume方法进行恢复,此时watchEffect的回调就会重新执行。不光watchEffect可以执行pause和resume方法,
watch一样也可以执行pause和resume方法。代码如下:
const runner = watch(count, () => {
if (count.value > 0) {
console.log(count.value);
}
});
runner.pause() // 暂停方法
runner.resume() // 恢复方法
7、 useId函数,目的是为每一个 vue 文件创建一个唯一的id,并保证在服务器和客户端渲染中保持稳定,可用于生成表单元素和可访问性属性的 ID,如下所示
<template>
<label :for="id">点我也能聚焦</label>
<!-- vue3.4同名简写 -->
<input :id type="text" placeholder="Enter text here">
</template>
<script setup>
import { useId } from "vue";
const id = useId();
console.log(id) // v:0
</script>
8,data-allow-mismatch,SSR中有的时候确实在服务端和客户端生成的html不一致,比如在DOM上面渲染当前时间,代码如下:
<template>
<div>当前时间是:{{ new Date() }}</div>
</template>
这种情况是避免不了会出现前面useId例子中的那种警告,有代码洁癖的我看着是真不爽。此时我们可以使用data-allow-mismatch属性来干掉警告,代码如下:
<template>
<div data-allow-mismatch>当前时间是:{{ new Date() }}</div>
</template>
9,Teleport组件新增defer延迟属性,Teleport 指令允许我们将 DOM 节点渲染到文档中的任意位置,这对于模态框或提示等元素特别有用。Vue 3.5 增强了 Teleport 的功能,使其可以更方便地管理目标节点。
<div id="target"></div>
<Teleport to="#target">被传送的内容</Teleport>
文案被传送的内容最终会渲染在id="target"的div元素中。在之前有个限制,就是不能将<div id="target">放在Teleport组件的后面。
这个也很容易理解DOM是从上向下开始渲染的,如果先渲染到Teleport组件。然后就会去找id的值为target的元素,如果找不到当然就不能成功的将Teleport组件的子节点传送到target的位置。
在3.5中为了解决这个问题,在Teleport组件上新增了一个defer延迟属性。加了defer延迟属性后就能将target写在Teleport组件的后面,代码如下:
<Teleport defer to="#target">被传送的内容</Teleport>
<div id="target"></div>
defer延迟属性的实现也很简单,就是等这一轮渲染周期结束后再去渲染Teleport组件。所以就算是target写在Teleport组件的后面,等到渲染Teleport组件的时候target也已经渲染到页面上了。
10,Suspense 组件是一个用于处理异步组件加载和渲染时延迟的有效工具。Vue 3.5 对 Suspense 的实现进行了优化,使得在等待异步数据时的用户体验更好。
<template>
<Suspense>
<template #default>
<AsyncComponent />
</template>
<template #fallback>
<div>Loading...</div>
</template>
</Suspense>
</template>
<script>
import { defineAsyncComponent } from 'vue';
const AsyncComponent = defineAsyncComponent(() => import('./AsyncComponent.vue'));
export default { components: { AsyncComponent } };
</script>
总结
对于开发者来说Vue3.5版本中还是新增了许多有趣的功能的, 这些功能在一些特殊场景中还是很有用的,总的来说这次的更新都是些小优化,没什么杀手级的特性,所以前端人们不用过于焦虑了吧。
标签:Vue,const,函数,vue,watch,watchEffect,3.5,组件,详解 From: https://blog.csdn.net/qq_36799762/article/details/142355373