【译】Announcing Vue 3.5 原文,看尤雨溪如何发布 Vue 新版本

2,239 阅读4分钟

大家好,我是双越老师,也是开源富文本编辑器 wangEditor 作者。

近期我正在致力于开发一个 Node 全栈 AIGC 知识库项目 划水AI ,包括文档管理、AI 写作、协同编辑等功能。真实上线,持续维护,有兴趣的同学欢迎点进去看看~

今天我来翻译一下 Vue 作者 尤雨溪 写的《Announcing Vue 3.5》 ,原文地址 blog.vuejs.org/posts/vue-3…

这不是一键机器翻译的,而是我自己一行一行亲自翻译过来的。第一保证可读性,第二我会在一些地方做出自己的解释。机器翻译的会很生硬,而且专业词汇会翻译错误,你一眼就能看出来。

Let's go.


2024.9.1

宣布 Vue3.5

今天我们很开心的宣布 Vue3.5 版本的发布,代号是“Tengen Toppa Gurren Lagann”

译者:这个代号貌似是动漫相关的,我不看动漫所以不了解,有知道的朋友可以评论解释一下。

这次小版本(minor release)升级没有断崖式的更新,既包含内部的升级优化,也包含一些有用的新功能。本文主要介绍一些重要功能,完整的更新和功能,从参考这里 the full changelog on GitHub

Reactivity System Optimizations 响应式系统优化

在 3.5 版本中,Vue 响应式系统又经历了一次重构,继续优化性能,内存使用率降低了 56% ,并且没有使用上的改变。这次重构也解决了在 SSR 期间因计算挂起而导致的陈旧的 computed 值和内存问题。

此外,3.5 版本还优化了针对大型数据、深层次数组的响应式追踪,在某些场景下最快有 10 倍的提速。

细节: PR#10397PR#9511

译者:这是内部改进,对使用者无影响。

Reactive Props Destructure 响应式属性解构

响应式属性解构在 3.5 版本中已经稳定了。现在这个功能已经可以默认使用了,在 <script setup> 中使用 defineProps 获取的变量并解构,支持响应式。而且,这个功能还简化了使用默认值来定义属性,直接使用 JS 原生的默认值语法。

之前

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 类似,监听一个属性解构变量,或者把他们传递给一个 compossable(译者:自定义 Hook)时,如果需要保持响应式,你需要用一个 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)

想要区分属性解构变量和普通变量,@vue/language-tools2.1 可以给出提示。

image.png

细节

  • 查阅 文档 有使用和注意事项
  • 查阅 RFC#502 有历史背景和设计原理

SSR Improvements 服务端渲染的改进

3.5 版本带来了一些期待已久的 SSR 改进。

Lazy Hydration

现在,你可以自定义策略,控制异步组件什么时候被 hydrated ,在 defineAsyncComponent() 中使用 hydrate API 来控制。例如,当它显示的时候(译者:即下面的 hydrateOnVisible() 返回值)才被 hydrated.

import { defineAsyncComponent, hydrateOnVisible } from 'vue'

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

这是一个比较基础的 API (译者:开发者不好直接使用),Nuxt 团队已经在此基础上封装了高级功能和语法糖,方便用户使用。

细节: PR#11458

useId()

useId() 可以生成唯一 ID ,在应用内不重复,而且可以跨服务端和客户端。可用于生成元素 ID 和 accessibility 属性,还可以在 SSR 中使用,这不会导致 hydration 不匹配。

译者:同一个数据客户端和服务端渲染不一致,和下文的 data-allow-mismatch 有关系。

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

const id = useId()
</script>

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

细节: PR#11404

data-allow-mismatch

某些情况下,客户端的值和服务端的值难免会不一样(如日期时间),现在我们可以通过 data-allow-mismatch 属性来屏蔽 hydration 不匹配的警告提醒。(译者:允许这里的 mismatch)

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

你还可以制定允许哪些类型可以 mismatch 并且把这个类型值传递给 data-allow-mismatch 属性值,类型可以是 textchildrenclassstyle, 和 attribute

Custom Elements Improvements 自定义元素的改进

3.5 版本修复了许多关于 defineCustomElement() API 的长期存在的问题,并且增加了许多自定义元素的新功能。

  • 支持自定义元素的 app 配置,通过 configureApp 选项
  • 增加 useHost, useShadowRoot, 和 this.$host API 用于访问自定义元素的宿主元素和 shadow 根节点
  • 传入 shadowRoot: false,支持在没有 shadowDOM 时,渲染自定义元素
  • 提供 nonce 选项,它会被添加由自定义元素注入的 <style> 标签上

这些新的 custom-element-only (译者:只能用在自定义元素)选项可以添加到 defineCustomElement 的第二个参数。

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

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

译者:Custom Elements 不太常用,我貌似还没用过呢,哈哈

Other Notable Features 其他重要功能

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 版本之前,我们推荐使用普通 ref 自己定义类型(译者:TS 类型)。之前的方式需要自定义定义 ref 以便编译器能理解,而且必须是静态类型。相比而言,useTemplateRef() 匹配运行时 ID ,因此支持动态绑定。(译者: 我也没搞定动态绑定啥意思,可能需要再看看文档和 demo)

译者:上面这一段我觉得翻译的不够精准细致,你可以自己去看原文。

@vue/language-tools 2.1 已经实现了 新语法的特殊支持,所以当你使用 useTemplateRef() 时候,它可以帮你自动完成和提示。

Deferred Teleport

对于 <Teleport> 组件,一个已知的限制就是:它在渲染时,它的目标元素必须存在。这可以防止用户传递内容给其他元素。

在 3.5 版本中,我们为 <Teleport> 组件增加了 defer 属性,这可以让它在下一轮渲染时加载这个组件,如下代码

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

这个特性需要 defer 属性,因为默认情况下我们要向后兼容。

细节: PR#11387

onWatcherCleanup()

3.5 新增了一个全局 API onWatcherCleanup() 可以在 watch 监听中注册一个 callback 回调函数。

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()
  })
})

对于 3.5 版本综合的改动和功能,你可以参考 GitHub 全部改动记录.

Happy hacking!


结束。以上就是原文的全部翻译,希望对大家有帮助。

最后,如果想参与学习真实线上项目,请看看我开发的 Node 全栈 AIGC 知识库项目 划水AI