VUE3.5版本解读

官网:Announcing Vue 3.5 | The Vue Point

2024年9月1日,宣布 Vue 3.5“天元突破:红莲螺岩”发布!

反应系统优化

在 3.5 中,Vue 的反应系统经历了另一次重大重构,在行为没有变化的情况下实现了更好的性能和显著改善的内存使用率(-56%)。重构还解决了 SSR 期间计算值挂起导致的过时计算值和内存问题。此 PR 重构了核心反应系统,以使用版本计数和受Preact 信号启发的双向链表数据结构。

此外,3.5 还优化了大型、深度反应阵列的反应性跟踪,在某些情况下可使此类操作速度提高 10 倍。

内存使用改进

给定一个有 1000 个引用 + 2000 个计算(1000 个链接对)+ 1000 个订阅最后一个计算的效果的测试用例,比较这些类实例使用的总内存:

  • 之前(3.4.19):1426k
  • 之后(此 PR):631k(-56%)

计算现在也仅在自身获得第一个订阅者时才延迟订阅 deps,并在失去所有订阅者时取消订阅。这意味着它们可以更可靠地进行垃圾收集,包括在 SSR 中。

响应式 Props 解构

在 Vue 3.5 中,响应式 props 的解构功能已经稳定下来,并且默认启用。这意味着在 <script setup> 中从 defineProps 解构的变量现在具有响应性。这一改进大大简化了使用默认值声明 props 的过程,并且利用了 JavaScript 的原生默认值语法。

<template>
  <div>
    <p>Name: {{ name }}</p>
    <p>Age: {{ age }}</p>
    <button @click="incrementAge">Age +1</button>
  </div>
</template>

<script setup>
import { ref, watch } from 'vue';

// 定义属性
const props = defineProps({
  name: {
    type: String,
    default: 'John'
  },
  age: {
    type: Number,
    default: 30
  }
});

// 创建响应式数据
const age = ref(props.age);

// 监听属性变化
watch(() => props.age, (newAge) => {
  age.value = newAge;
});

// 更新年龄的方法
const incrementAge = () => {
  age.value++;
};
</script>

解构 props 变量(例如 count)的访问会被编译器自动编译成 props.count,从而保持响应性。这意味着当我们访问解构后的变量时,Vue 会自动对其进行跟踪。 如果需要在保留响应性的同时监视解构的 prop 变量或将其传递到可组合函数中,需要将其包装在 getter 中。这一改进使得处理 props 变得更加直观和高效。

这一改进大大简化了代码,使得 props 的声明和使用更加直观和简洁。我们不再需要使用 toRefs 或其他复杂的模式来保持响应性。

SSR 改进

在 Vue 3.5 中,服务器端渲染(SSR)得到了一些重要的改进,特别是在懒惰水合、useId() API 和 data-allow-mismatch 属性方面。这些改进旨在提高 SSR 的性能、稳定性和开发体验。

懒惰水合(Lazy Hydration)

控制水合策略

在 Vue 3.5 中,异步组件的水合策略可以通过 defineAsyncComponent API 的 hydrate 选项来控制。懒惰水合是指组件在首次渲染时不会立即进行水合(hydration),而是在某些条件满足时(例如组件可见时)才进行水合。

import { defineAsyncComponent } from 'vue';

const LazyComponent = defineAsyncComponent({
  loader: () => import('./MyComponent.vue'),
  hydrate: {
    when: 'visible', // 仅在组件可见时进行水合
  },
});

核心 API 设计得较低级别,而 Nuxt 团队已在此特性的基础上构建了更高级别的语法糖,使得懒惰水合的实现更加简单和直观。

useId() API

useId() 是一个 API,可用于生成每个应用程序唯一的 ID。这些 ID 在服务器和客户端渲染过程中保持稳定,确保不会导致水合不匹配。

import { useId } from 'vue';

const uniqueId = useId();

应用场景

这些唯一 ID 可用于生成表单元素和可访问性属性的 ID,确保在 SSR 应用程序中使用时不会导致水合不匹配。

data-allow-mismatch 属性

抑制水合不匹配警告,在某些情况下,客户端值不可避免地与服务器对应值不同(例如日期)。Vue 3.5 引入了 data-allow-mismatch 属性,用于抑制由此产生的水合不匹配警告。

<div data-allow-mismatch>
  <!-- 内容 -->
</div>

还可以通过为 data-allow-mismatch 属性提供值来限制允许的不匹配类型。可能的值包括 text、children、class、style 和 attribute。

<div data-allow-mismatch="text">
  <!-- 内容 -->
</div>

自定义元素改进

在 Vue 3.5 中,自定义元素(Custom Elements)得到了显著的改进,修复了许多与 defineCustomElement API 相关的长期存在的问题,并添加了许多新功能。这些改进使得使用 Vue 创建自定义元素更加灵活和强大。

自定义元素的应用程序配置

通过选项支持自定义元素的应用程序配置,在 Vue 3.5 中,你可以通过 configureApp 选项来配置自定义元素的应用程序。这使得你可以在定义自定义元素时进行更细粒度的配置。

import { defineCustomElement } from 'vue';

const MyElement = defineCustomElement({
  // 组件选项
  template: '<div>Hello, World!</div>',
  configureApp: (app) => {
    // 配置应用程序
    app.config.globalProperties.$myGlobal = 'global value';
  },
});

customElements.define('my-element', MyElement);

访问宿主元素和影子根

添加 useHost()、useShadowRoot() 和 this.$host API,Vue 3.5 添加了 useHost() 和 useShadowRoot() 组合函数,以及 this.$host API,用于访问自定义元素的宿主元素和影子根。

import { defineCustomElement, useHost, useShadowRoot } from 'vue';

const MyElement = defineCustomElement({
  setup() {
    const host = useHost();
    const shadowRoot = useShadowRoot();

    console.log('Host Element:', host.value);
    console.log('Shadow Root:', shadowRoot.value);

    return () => <div>Hello, World!</div>;
  },
});

customElements.define('my-element', MyElement);

支持安装没有 Shadow DOM 的自定义元素

通过传递 shadowRoot: false 支持安装没有 Shadow DOM 的自定义元素,在 Vue 3.5 中,你可以通过传递 shadowRoot: false 选项来支持安装没有 Shadow DOM 的自定义元素。

import { defineCustomElement } from 'vue';

const MyElement = defineCustomElement({
  template: '<div>Hello, World!</div>',
  shadowRoot: false, // 不使用 Shadow DOM
});

customElements.define('my-element', MyElement);

支持提供 nonce 选项

支持提供一个 nonce 选项,该选项将附加到 <style> 自定义元素注入的标签上,Vue 3.5 支持提供一个 nonce 选项,该选项将附加到 <style> 自定义元素注入的标签上,以增强安全性。

import { defineCustomElement } from 'vue';

const MyElement = defineCustomElement({
  template: '<div>Hello, World!</div>',
  nonce: 'my-nonce-value', // 设置 nonce 值
});

customElements.define('my-element', MyElement);

仅自定义元素选项

通过第二个参数传递仅自定义元素选项,在 Vue 3.5 中,你可以通过第二个参数传递仅自定义元素选项,以进一步定制自定义元素的行为。

import { defineCustomElement } from 'vue';

const MyElement = defineCustomElement(
  {
    template: '<div>Hello, World!</div>',
  },
  {
    shadowRoot: false, // 不使用 Shadow DOM
    nonce: 'my-nonce-value', // 设置 nonce 值
  }
);

customElements.define('my-element', MyElement);

其他显著特点

useTemplateRef() API

获取模板引用的新方法

Vue 3.5 引入了一种通过 useTemplateRef() API 获取模板引用的新方法。这种方法通过运行时字符串 ID 匹配引用,因此支持动态引用绑定到变化的 ID。

<template>
  <div>
    <input ref="inputRef" type="text" />
  </div>
</template>

<script setup>
import { useTemplateRef } from 'vue';

const inputRef = useTemplateRef('inputRef');

console.log(inputRef.value); // 获取 input 元素的引用
</script>

与旧方法的对比

在 3.5 之前,我们建议使用变量名与静态 ref 属性匹配的普通引用。旧方法要求 ref 属性可由编译器分析,因此仅限于静态 ref 属性。相比之下,useTemplateRef() 通过运行时字符串 ID 匹配引用,因此支持动态引用绑定到变化的 ID。

语言工具支持

@vue/language-tools 2.1 还实现了对新语法 useTemplateRef() 的特殊支持,因此在使用时您将根据 ref 模板中存在的属性获得自动完成和警告。

延迟传送(Deferred Teleport)

内置 <Teleport> 组件的限制

内置 <Teleport> 组件的一个已知限制是,其目标元素必须在传送组件挂载时存在。这阻止用户在传送后将内容传送到 Vue 渲染的其他元素。

引入 defer prop

在 Vue 3.5 中,我们引入了一个 defer prop,用于在当前渲染周期之后挂载 <Teleport>,因此现在可以正常工作:

<template>
  <div>
    <Teleport to="#target" defer>
      <div>Teleported Content</div>
    </Teleport>
  </div>
</template>

<script setup>
import { Teleport } from 'vue';
</script>

默认行为

此行为需要 defer prop,因为默认行为需要向后兼容。

onWatcherCleanup() API

在观察者中注册清理回调

Vue 3.5 引入了一个全局导入的 API onWatcherCleanup(),用于在观察者中注册清理回调。

import { watch, onWatcherCleanup } from 'vue';

watch(
  () => someValue,
  (newValue, oldValue, onCleanup) => {
    const cleanup = () => {
      // 清理逻辑
    };

    onWatcherCleanup(cleanup);
  }
);

应用场景

onWatcherCleanup() 可以在观察者中注册清理回调,确保在观察者被停止或重新运行时执行清理逻辑,避免内存泄漏和其他潜在问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Onlooker﹒

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

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

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

打赏作者

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

抵扣说明:

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

余额充值