新版 Vue 3.5 来了,我们需要去了解它的一些新特性。此次要版本不包含重大更改,包括内部改进和实用的新功能。首先我们来回顾下历史:
-
2014年02月:首次公开宣布。
-
2015年10月:Vue v1.0 发布。
-
2016年10月:Vue v2.0 发布,带来虚拟DOM、SSR、指令系统、组件系统、响应式系统、自定义事件等众多特性和优化,同时尤雨溪开始全职投入 Vue 开发。
-
2020年09月:Vue v3.0 发布,从2018年09月启动开发,历时两年,带来Composition API、更小的体积、更快的性能以及跨平台支持等特性和优化。
-
2023年12月:Vue v2.0 停止维护,生命周期结束,不再得到官方支持,受到前端开发者广泛关注。
-
2023年12月:Vue v3.4 发布,解析器速度提高 2 倍,SFC 构建性能提升。
下面我们将根据官方更新日志来看看 Vue 3.5 将带来哪些新功能。
Reactive Props Destructure
随着 3.5 版本的发布,响应式属性解构将成为一个稳定特性。这个特性允许你从defineProps宏中解构属性而不会失去响应性。
import { watchEffect } from 'vue'const { count } = defineProps(['count'])
watchEffect(() => { // this will log every time the count prop changes from parent
console.log(count)
})
尤雨溪说:"几乎每一个在实际项目中大规模使用响应式属性解构的开发者都反馈良好。他们表示很喜欢这个特性,希望看到这个特性被稳定下来。"
对于那些不想使用这个特性的人,也提供了一个标志来关闭它。可以在 vite.config.js 文件中配置 propsDestructure 属性来关闭它。
// vite.config.jsexport default {
plugins: [
vue({
script: {
propsDestructure: false
}
})
]}
useTemplateRef
当前声明模板引用的过程如下:
<script setup>
// first you defined a ref with the value of undefined or null
// and name the resulting vartiable whatever you want
const divEl = ref();
</script>
<template>
<!-- Then use the same name as the value for a `ref` attribute
somewhere in the template
-->
<div ref="divEl" ></div>
</template>
这种方法存在两个问题:
-
有时候会令人困惑。divEl 是响应式数据还是DOM元素?如果你有一个命名模板引用的约定,这并不是坏事,但最终你还是要在模板中查找匹配的ref=来确保。
-
此外,这限制了你只能在组件脚本设置部分定义模板引用。这意味着需要访问DOM元素的组合函数必须将模板引用作为参数接受。
现在,使用useTemplateRef,这两个问题都得以解决。
// MyComposable
export const useMyComposable = (options = { templateRef: 'el' })=>{
// very clearly a template ref due to the name of the function
const theEl = useTemplateRef(options.templateRef);
}
// MyComponent
<script setup>
// no need to define the template ref in the component
// that can be the composables job
useMyComposable()
useMyComposable({ templateRef: 'el2' })
</script>
<template>
<div ref="el"></div>
<div ref="el2"></div>
</template>
useId
新的 useId 实用函数返回一个在服务器渲染和客户端渲染过程中保持稳定的唯一ID(这样可以减少 hydration 不匹配的问题出现在您的应用程序中的方式,太棒了!)。这对于与表单元素属性(如for和id)以及accessibility属性一起使用非常nice。
<!--MyCustomInput-->
<script setup>
defineProps({
label: String
help: String
//...
});
const inputId = uesId();
const helpTextId = useId();
</script>
<template>
<label :for="inputId">{{label}}</label>
<input :id="inputId" :aria-describedby="helpTextId"/>
<p :id="helpTextId">{{ help }}</p>
</template>
hydrate {#lazy-hydration}
hydrate
异步组件现在可以通过 API选项指定策略来控制何时进行水合defineAsyncComponent()
。例如,仅在组件可见时进行水合:
import { defineAsyncComponent, hydrateOnVisible } from 'vue'
const AsyncComp = defineAsyncComponent({
loader: () => import('./Comp.vue'),
hydrate: hydrateOnVisible()
})
核心 API 有意设计得较低级别,而 Nuxt 团队已在此特性的基础上构建了更高级别的语法糖。
data-allow-mismatch
{#data-allow-mismatch}
如果客户端值不可避免地与服务器对应值不同(例如日期),我们现在可以使用data-allow-mismatch
属性来抑制由此产生的水合不匹配警告:
<span data-allow-mismatch>{{ data.toLocaleString() }}</span>
您还可以通过为属性提供值来限制允许的不匹配类型,可能的值包括text
、children
、class
、style
和attribute
。
自定义元素改进 {#custom-elements-improvements}
3.5 修复了许多与 API 相关的长期存在的问题defineCustomElement()
,并添加了许多使用 Vue 创作自定义元素的新功能:
-
通过选项支持自定义元素的应用程序配置
configureApp
。 -
添加
useHost()
、useShadowRoot()
和this.$host
API,用于访问自定义元素的宿主元素和影子根。 -
通过传递支持安装没有 Shadow DOM 的自定义元素
shadowRoot: false
。 -
支持提供一个
nonce
选项,该选项将附加到<style>
自定义元素注入的标签上。
defineCustomElement
这些新的仅自定义元素选项可以通过第二个参数传递:
import MyElement from './MyElement.ce.vue'
defineCustomElements(MyElement, {
shadowRoot: false,
nonce: 'xxx',
configureApp(app) {
app.config.errorHandler = ...
}
})
其他显著特点 {#other-notable-features}
useTemplateRef()
{#usetemplateref}
3.5 引入了一种通过API 获取模板引用的新方法useTemplateRef()
:
<script setup>import { useTemplateRef } from 'vue'const inputRef = useTemplateRef('input')</script><template> <input ref="input"></template>
在 3.5 之前,我们建议使用变量名与静态ref
属性匹配的普通引用。旧方法要求ref
属性可由编译器分析,因此仅限于静态ref
属性。相比之下,useTemplateRef()
通过运行时字符串 ID 匹配引用,因此支持将动态引用绑定到不断变化的 ID。
@vue/language-tools
2.1 还实现了对新语法的特殊支持useTemplateRef()
,因此在使用时您将根据ref
模板中存在的属性获得自动完成和警告:
延迟传送 {#deferred-teleport}
内置<Teleport>
组件的一个已知限制是,其目标元素必须在传送组件挂载时存在。这阻止用户在传送后将内容传送到 Vue 渲染的其他元素。
在 3.5 中,我们引入了一个在当前渲染周期之后挂载它的defer
prop <Teleport>
,因此现在可以正常工作:
<Teleport defer target="#container">...</Teleport>
<div id="container"></div>
此行为需要defer
prop,因为默认行为需要向后兼容。
onWatcherCleanup()
{#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()
})
})
内存改进 === 更快的应用程序
尤雨溪说,"第一个重大变化是内部响应式重构的第二次尝试"。这次重构意味着内存使用量减少了 60%,这对任何使用大型反应数据数组的应用程序来说都是巨大的胜利。事实上,Vue 团队还特别注意对许多常用数组方法进行了单独优化,从而使数组迭代速度提高了 10 倍之多。
SSR 相关改进
虽然尚未出现在变更日志中,但 Evan 提到其他与 SSR 相关的改进即将推出,包括:
-
使用 async 组件作为边界的懒式水合。这意味着您可以在定义 async 组件时定义客户端水合组件的 JavaScript 发送到浏览器的时间。
-
有选择性地允许水合不匹配,从而更轻松地处理客户端和服务器之间永远不会相同的数据(如日期)。
总结
Vue 3.5 的新特性不仅提升了开发效率和用户体验,还通过性能优化和 SSR 相关改进,为开发者提供了更加强大和灵活的工具。