一文超详解锁 Vue 3.5新特性

前端人的苦恼叕来了,前端技术隔三岔五的更新,学习别想停了,趁着中秋即将来临卷起来吧(说好的中秋假期咱不卷的呢)。就在这个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版本中还是新增了许多有趣的功能的, 这些功能在一些特殊场景中还是很有用的,总的来说这次的更新都是些小优化,没什么杀手级的特性,所以前端人们不用过于焦虑了吧。

原文链接

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序媛凡芽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值