Vue3.5发布,看一看有哪些值得注意的功能!

723 阅读3分钟

今天我们很高兴地宣布发布 Vue 3.5 "Tengen Toppa Gurren Lagann"!

这个小版本更新没有引入任何破坏性更改,并且包含了内部改进和一些实用的新功能。我们将在本文中介绍一些亮点

响应式系统优化

在 3.5 版本中,Vue 的反应式系统经历了重大重构,实现了更好的性能并显著减少了内存使用(-56%),而且没有改变任何行为。此次重构还解决了在服务端渲染(SSR)过程中由于挂起的计算属性导致的陈旧计算值和内存问题。

此外,3.5 还优化了对大型、深度响应数组的跟踪,在某些情况下使这类操作的速度提升了高达 10 倍

响应式 Props 解构

在 3.5 版本中,响应式 Props 解构功能已经稳定,并默认启用。现在,在 <script setup> 中从 defineProps 调用解构的变量将具有响应式。值得注意的是,此功能通过利用 JavaScript 的原生默认值语法,大大简化了声明具有默认值的 Props:

之前

const props = withDefaults(
  defineProps<{
    count?: number
    msg?: string
  }>(),
  {
    count: 0,
    msg: 'hello'
  }
)

以后

const { count = 0, msg = 'hello' } = defineProps<{
  count?: number
  message?: string
}>()

访问解构变量

访问解构后的变量(例如 count)会被编译器自动转换为 props.count,因此它们在访问时会被跟踪。类似于 props.count,如果需要观察解构后的 Props 变量或将它传递给一个组合式函数(composable)以保留其响应性,则需要将其包裹在一个 getter 中。

以下是一个示例:

watch(count /* ... */)
//    ^ results in compile-time error

watch(() => count /* ... */)
//    ^ wrap in a getter, works as expected

// composables should normalize the input with `toValue()`
useDynamicCount(() => count)

对于希望更好地区分解构后的 Props 和普通变量的开发者,@vue/language-tools 2.1 提供了一个可选设置,用于启用内嵌提示(inlay hints):

image.png

SSR 改进

3.5 版本带来了一些长期被请求的服务器端渲染(SSR)改进。

Lazy Hydration

异步组件现在可以通过 defineAsyncComponent() API 的 hydrate 选项来控制何时进行水合(水合是指将服务器端渲染(SSR)生成的静态 HTML 内容转换为动态的、可交互的 Web 组件的过程)。例如,仅在组件变为可见时才进行水合:

import { defineAsyncComponent, hydrateOnVisible } from 'vue'

const AsyncComp = defineAsyncComponent({
  loader: () => import('./Comp.vue'),
  hydrate: hydrateOnVisible()
})

核心 API 故意设计得较为底层,Nuxt 团队已经在这一特性基础上构建了更高层次的语法糖。

useId()

useId() 是一个可以用来生成在整个应用中唯一的 ID 的 API,这些 ID 在服务器端和客户端渲染时保证是稳定的。它可以用于生成表单元素和无障碍属性的 ID,并且可以在 SSR 应用中使用而不会导致水合不匹配:

<script setup>
import { useId } from 'vue'

const id = useId()
</script>

<template>
  <form>
    <label :for="id">Name:</label>
    <input :id="id" type="text" />
  </form>
</template>

data-allow-mismatch

在某些情况下,如果客户端的值不可避免地与服务器端的值不同(例如日期),我们现在可以通过使用 data-allow-mismatch 属性来抑制由此产生的水合不匹配警告:

<span data-allow-mismatch>{{ data.toLocaleString() }}</span>

你还可以通过为该属性提供一个值来限制允许的不匹配类型,可能的值包括 textchildrenclassstyle 和 attribute

Custom Elements Improvements

3.5 修复了许多与 defineCustomElement() API 相关的长期问题,并添加了多项新的功能,以便使用 Vue 编写自定义元素:

  • 通过 configureApp 选项支持自定义元素的应用配置。
  • 添加 useHost()useShadowRoot() 和 this.$host API 以访问自定义元素的宿主元素和影子根(shadow root)。
  • 支持通过传递 shadowRoot: false 来挂载没有 Shadow DOM 的自定义元素。
  • 支持提供 nonce 选项,该选项将附加到由自定义元素注入的 <style> 标签上。

这些新的仅适用于自定义元素的选项可以通过 defineCustomElement 的第二个参数传递:

import MyElement from './MyElement.ce.vue'

defineCustomElements(MyElement, {
  shadowRoot: false,
  nonce: 'xxx',
  configureApp(app) {
    app.config.errorHandler = ...
  }
})

其他值得注意的功能

useTemplateRef()

3.5 引入了一种新的获取模板引用(Template Refs)的方式,通过 useTemplateRef() API 实现:

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

const inputRef = useTemplateRef('input')
</script>

<template>
  <input ref="input">
</template>

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

@vue/language-tools 2.1 也实现了对新语法的特殊支持,因此在使用 useTemplateRef() 时,你会根据模板中是否存在 ref 属性获得自动补全和警告:

image.png

Deferred Teleport(延迟传送)

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

在 3.5 版本中,我们为 <Teleport> 添加了一个 defer 属性,使其在当前渲染周期之后挂载,因此现在这种方式可以工作了:

<Teleport defer target="#container">...</Teleport>
<div id="container"></div>

这种行为需要 defer 属性,因为默认行为需要与之前的版本兼容。

onWatcherCleanup()

3.5 引入了一个全局导入的 API onWatcherCleanup(),用于在监听器中注册清理回调函数:

import { watch, onWatcherCleanup } from 'vue'

  watch(id, (newId) => {
    const controller = new AbortController()

    fetch(`/api/${newId}`, { signal: controller.signal }).then(() => {
      // callback logic
    })

    onWatcherCleanup(() => {
      // abort stale request
      controller.abort()
    })
  })

译自:blog.vuejs.org/posts/vue-3…