非常高兴再次来到这里。这是我们第一次参加 VueConf US 的地方,那真是一段美好的回忆。事实上,自从大流行以来,我就没有去过 VueConf US。事实上,整件事就开始于我们,等等,哪一个,2019 年,2020 年?奥斯汀,是的,就在奥斯汀。就像,我回到家,新冠病毒刚刚开始。在美国爆发,从那以后我已经有三年没有回到过 VueConf 了,非常高兴能够亲自参加,看到大家。好吧,让我们开始分享。
所以这次演讲将主要关注 Vue 核心中正在发生的事情以及我们下一步计划做什么。显然,两周前的重大新闻,我们发布了 Vue 3.3,这是相当长一段时间以来最早的 Milo 版本之一。所以在这个过程中,我们在回到 Vue 核心之前主要关注了很多。在 3.2 之后,在 3.3 之前,有很长一段时间我们非常关注核心之外的工具和开发体验。也就是说我们在 Vite 上投入了大量时间。在座有多少人在使用 Vite?呜,太好了。那些还没有尝试过的人,你应该开始尝试一下。我们花了很多时间来
改进 Volar 的 IDE 体验。因此,如果你一直在 Vue 中使用 VS code,您可能已经注意到我们,我们一直在不断改进 typescript 集成和一切。但是在那之后,我们意识到,好吧,有一些问题,为了获得最好的 TypeScript 集成体验,我们需要在 Vue 核心中做更多的事情。这就是 3.3 的主要焦点。这就是使用 script setup 和 typescript 时的开发体验。
从历史上看,Vue 尤其是它的选项 API,所以当我们设计带有选项 API 的 Vue 时,TypeScript 根本就不是一个东西。所以最初的 API 设计一开始并没有太多类型推断相关的概念。
如果使用选项 API、TypeScript 的经验,我们已经以某种方式使其发挥作用,对吧?但这并不完美,我们花了很多努力,尤其是,我们甚至不得不贿赂 TypeScript 团队,以便他们开发出
这个称为 BizType 的东西,这样我们就可以真正获得正确的类型推断。这里面有选项 API 等等。
但是,我们希望,当我们引入 composition API 时,我们首先想到的是,好吧,我们需要让 typescript 集成 Vue 中的首要概念。我不想说你必须使用 TypeScript。显然,即使没有 TypeScript,我们仍然专注于确保开发体验也是愉快的。但事实是更多的人,更多的 Vue 用户,他们想使用 typescript,对吧?如果他们想要将 Vue 与 TypeScript 结合使用,并且他们没有良好的开发体验,那么就由我们来承担。所以这一直是我们确保一切顺利的一个重点。
因此,如果您不将 TypeScript 与 Vue 一起使用,很多内容可能没有多大意义,但是请耐心听我说,我强烈建议,您是否打算使用 Vue 构建一个长期可维护的产品,并且你有一个团队,你有多个人在同一个项目上工作,你应该考虑开始使用 typescript,我认为这将使你从长远来看受益。
好的,所以我们在 2.3 中解决的第一件事是一个长期存在的问题,即当您使用 TypeScript 进行 script setup 时,因此当您使用定义的 props 时,以前,您可以使用仅类型语法来生成运行时 props,对吗??所以编译器实际上会查看您提供的用于定义 props 的类型,并生成必要的相应运行时 props 声明。因此以前的限制是你不能只是导入一个外部类型,然后将其推入定义的 props 中。好吧,现在 Vue 3.3 已经支持了,还添加了对一堆复杂类型的支持,如交叉点和地图类型等等。
因此,一般来说,在脚本中使用这种仅类型的 props 声明应该会更容易设置。然后通过脚本设置,您现在也可以使用泛型。因此,如果您使用 TypeScript,您就会知道,TypeScript 中的泛型功能本质上允许您声明类似于占位符的类型。你可能会说,我事先不知道这个类型是什么,但是但是这两个 props 之间存在一些相关的约束。因此,这个选定的项目将是 T 类型。而这个项目道具将是该类型的数组。该类型究竟是什么,它可以由用户决定,具体取决于用户传递的内容。
因此,因此,如果用户将 selected 作为 bling 传入,则项目也必须满足类型约束,因此它必须是 bling 数组。如果说用户在这里传入一个字符串,然后传入一个 blings 数组,这将是一个类型错误,对吧?因此,我们设计语法的方式是,您现在可以在脚本标记上添加通用属性,它的工作原理就像 typescript 中的尖括号通用语法一样,这意味着您可以使用普通 typescript 中可以进入该尖括号的任何内容。所以你可以说类型约束。您可以拥有扩展类型约束的类型。它可以有多个类型参数。所以它在大多数情况下都有效,它应该像你期望的那样工作,好吗?然后我们还改进了 defy emits。
所以之前当你使用仅类型语法的 defy emits 时,这有点尴尬,因为我们希望类型完全锁定这个发射函数的返回类型,对吧?this 的类型是一个函数,可以接受第一个参数是可以不同的事件名称。然后您可以有任意附加参数列表。所以这实际上是一个带有调用签名的对象,对吧?这不是你经常写的东西。我听到很多人说当我使用定义发出类型语法时,我每次都必须在文档中查找。所以我们对它进行了一些简化,以便您可以改用此语法。因此,您不能给它一个对象类型,而 key 是事件的名称,可以是接受的参数的数组或元组类型。因此,如果它不接受任何参数,只需给它一个空数组。
这里你注意到这个 ID 栏。这实际上在 TypeScript 中称为带标签的双拉语法。因此,您实际上可以为元组类型中的每个元素命名,这样当您将鼠标悬停在它上时,您实际上可以知道第一个参数是 ID,第二个参数是其他参数。好的,如果您使用的是 Vue 2.7,这也支持您的 Vue 宏。我们在 3.3 中采用的相当多的功能实际上首先在 Vue Macros 中上线,它本来是一个社区项目,但作者 Kevin 现在是核心团队成员,他也在 3.3 中做了很多贡献。
我们现在还支持具有定义插槽的类型化插槽。所以这是 script setup 中的一个新宏。再次。您可以使用对象语法定义插槽。如果您在编程级别使用 Vue 插槽,例如在渲染函数级别,您就会知道插槽在内部表示为函数。一个槽从父槽传递给子槽就像传递一个函数。这里的插槽类型被表示为函数。第一个参数是这个插槽期望获得的 props。一旦你声明了这些,volar 就会给你输入提示。例如,从父级消耗这个组件。你向下传递一个插槽,一个插槽接收 props。这些 props 将使用您在此处提供的信息正确输入。所以从技术上讲,我们也可以检查插槽的存在。假设这里我们将两个插槽设置为可选。您可以删除问号并使其成为必需的插槽。因此,这项检查目前尚未实施,但已在路线图上我们最终会将其记录在日志中。同样,这个宏,你可以在 2.7 中得到支持。你的 Vue 宏。然后还有一些实验性功能。所以结构的反应性 props,这最初是反应性转换提案的一部分,不幸的是,我们最终决定放弃。但是我们想把这部分从提案中分离出来,因为它的影响范围较小。所以权衡变得有点不同,这使得这可能更合理地作为实际功能落地。
因此,以前定义的 props 的痛点之一是当您想使用类型语法声明默认值时,您必须将这种尴尬的情况与默认定义的 props 一起使用。这是我们一直在寻找改进方法的事情之一。也许该结构允许您在进行此结构化时使用本机 JavaScript 默认语法来完成此操作。
现在,结构化的问题是,默认情况下,当你解构某些东西时,它会断开反应性,对吧?如果你使用 Vue 的时间足够长,你知道如果你有一个响应式对象,并且可用它来构造一个属性,那这个属性保持不变。它不再与原始对象连接。但是这个特性,反应性 props 解构,它所做的是通过一些编译时处理,我们能够使解构绑定保持反应性。所以它将连接到从父级传递的 props。所以如果父 props 发生变化,这里的这个本地绑定实际上是被动更新的。所以编译器将其重写为类似 props.foo 的东西。所以它保持了点的访问权限,以便全程跟踪反应性。。
现在,当您声明 props 时,这为您提供了更简洁、更简单的语法。也给你一个非常好的人体工程学改进。现在,此功能仍被视为实验。因此,它需要明确选择大多数这些功能,如果您还没有查看过,你应该检查 3.3 版本的博客文章。它提供了有关如何启用这些功能并选择加入这些功能的更多详细信息。
下面是一个响应式 props 解构的例子。因此,当您在 watchEffect 内部使用解构的 prop 绑定时,当您在此处访问它时,它会将其作为该效果的依赖项进行跟踪。因此,只要父级的消息发生更改,此 watchEffect 就会重新运行。
然后是这个 DefineModel 功能。所以,如果之前当你在创作一个组件,一个表单组件时,你正在包装一个原生输入元素,你说我想要一个自定义输入,并且我希望这个自定义输入组件,支持 V 模型,对吗?如果你以前做过,你知道这在过去有点麻烦。您需要做的是首先需要声明一个名为 model value 的 prop。然后您需要有一个名为 update:modelValue 的相应发出事件,然后在内部,您需要对原生输入元素进行手动绑定。您需要将 props 与其值绑定。然后需要像这样处理你的事件发出。因此,所有这些都可以通过称为 DefineModel 的新宏来简化。哇哦。哇,你只要定义一个模型,它就成为一个 ref,而这个 ref 只是,您只需读取它的值,您就可以更新它的值。更好的部分是当您尝试将其连接到本机输入时,您只需使用本机 V 模型,仅此而已。因此,如果您正在进行大量这些包装自定义输入元素,这是一个很大的质量改进。
好的,另一件事是 defineOptions。所以以前当你使用脚本设置,但是你仍然想定义一些自定义选项,比如继承的属性,或者你使用的是外部库,而库就像,你需要把这个自定义选项传给我。以前做到这一点的唯一方法是使用单独的脚本标记。现在您可以在相同的脚本设置块中使用 DefineOptions。因此,不需要两个单独的脚本块。哦,好吧。那么你的 toRef 和 toValue 就有了 getter 支持。所以这个需要一点解释。所以最初的问题是,当你使用 competition API 时,我们开始使用越来越多的可组合包,对吗?你们这里有多少人用过 Vue?很多人,对吧?因此,Vue 中使用的许多函数或您编写的自定义可组合项通常会期望您传递某种反应状态,对吧?为了方便起见,通常接受的参数类型称为 “也许休息”。所以它可以是一个值,也可以是一个包裹。
对。一个很常见的情况是你有一个道具。你有 prop,对吧,你想把一个 prop 传递到可组合项中,但是 prop 本身不是一个引用。所以你需要把它变成一个 ref 来传递它,以保持反应性。这在过去有点痛苦,对吧。我们想出了各种各样的解决方法,比如你用 toRef 从 props 上取下一些东西。您可以使用计算来创建引用,或者您可以,好吧,对吧?
所以这是两种不同的方法,但是它们有缺点。第一个当您在 prop 上有一个深度嵌套的属性并且您使用 toRev 时,它不会处理 Foo 的存在。所以如果 Foo 最初不存在,就像它一开始未定义一样,这个它就会失败。所以你使用计算但计算的,实际上有一些隐藏的成本,因为它必须这样做,每个计算的属性实际上必须跟踪其依赖项。所以通常我们建议使用 compute,compute 属性的初衷是让你能够缓存一些昂贵的操作,但是仅仅访问一些属性并不昂贵,它实际上最终会花费比必要的更多内存。对,所以如果你能像这样传递一个 getter,那就不好了。这就足够了,它高效、简洁,您输入的内容更少,您不需要导入计算它。因此,为了在您的可组合项中支持这一点,我们提供了一个名为 toValue 的新助手。所以这本质上是 onRef 的一个略有不同的版本。这和 onRef 的区别在于,如果传递的值是一个函数或 getter 函数,它实际上只会调用它来获取返回值。那么为什么我们不在投票获取器中提供 onRef 支持呢?因为您可以将一个函数传递给 onRef 并期望原封不动地返回该函数,对吗?所以我们不能把它作为断链运输。所以我们需要在这里引入一个新函数,让 toValue 来做到这一点。与 toValue 相对的是 toRef,,它现在也支持获取 getter 或 value 或 ref。
因此,这里的常见模式是在您的可组合项中,Foo 类型可能是 ref 或 getter 编号。因此,它可以是一个数字或一个返回数字或包装数字的引用的 getter。toValue 只会将其标准化为始终成为数字,而不管它是什么。所以你不需要做很多条件检查,你只需总是 toValue,它就会给你返回值。所以这简化了用法,而 toRef 和 toValue 只是归一化的两个相反方向。因此,无论您给出一个值或 ref 或 getter,都使用 toRef 标准化为 ref,使用 toValue 标准化为值。因此我们希望这种模式能够在社区可组合库中采用,使其成为普遍做法。
还有关于 JSX 的一件小事,所以如果您不使用 JSX,这可能根本不关心您。如果你这样做,我们在 3.3 中的导入源中支持名为 JSX 的东西。想要这样做的原因之一是因为目前,为了使 Vue JSX 工作,我们默认在全局注册它的类型。如果您在同一个项目中使用 Vue 和 react,这最终会导致冲突。我不知道你为什么要这么做,但有些人会。这有点像,我们没有充分的理由说好吧,由于类型冲突,这不是你可以做的事情。所以我们想解决这个问题。做到这一点的方法是能够允许您显式指定要使用哪个类型、哪个库中的 JSX 类型。
这现在支持 3.3,但全局注册仍然存在,以避免成为一个突破性的变化。所以我们的迁移过程是,将 JSX 与 Vue 一起使用,并且类型为 fern,那么您应该开始指定这一点,以便在 3.4 中我们将删除默认的全局注册,并且一切都会继续工作,好吗?所以这只是一个小小的提醒。好的,3.3 就足够了。
我故意走得更快一点,这这样我们就可以更加关注接下来会发生什么。所以 VitePress。我们刚刚进入 1.0 测试版,我们打算让它变成 1.0。你们中有多少人知道 Vitepress 是什么?用过吗?如果你有一个项目,更多的人应该尝试一下。所以如果你不知道,Vitepress 是一个静态站点生成器。你们中有多少人听说过Vuepress?好的。所以 VitePress 本质上是 VuePress 的精神继承者,除了它更轻、更快、性能更强之外。默认主题看起来更好。所以它是一个静态站点生成器,它允许您将一堆 Markdown 文件放在一起,它只是为您生成一个漂亮的网站。好吗?它也是高度可定制的。所以你可以用它来做自定义主题。
其实 VJs 官方网站,Vitestep 网站,rollups 网站,很多项目,开源项目,他们的文档网站现在都是建立在 Vitepress 之上的。如果你有你的开源网站,你需要一个文档网站,最简单、最快的方法。
事实上,这是我们真正引以为豪的事情。因此,就 Vue 核心而言,我们确实计划进行一轮问题和 PR 清理,因为我们已经有一堆问题堆积起来。我们只想进行一轮大规模的数据压缩。以及即将推出的 3.x 未成年人计划。会议赛季结束后的第一要务。因此,这可能从 6 月份开始,将开始稳定悬念。
我们还想移动安全传送。这是核心团队成员 Guillane 一直在努力的事情之一,我们打算将其移入核心,这样即使你的传送目标还没有安装,更高效的计算和验证。
这是核心团队成员 Johnson 一直致力于提高计算机性能,减少不必要的重新计算的工作。然后是服务器端渲染改进。惰性水合需要在核心。这是我们长期以来想要的东西。然后我们想对常见的课程,水合作用不匹配进行验证。如果您使用过 Nucks。您过去可能遇到过这种情况。假设你把一个 P 标签放在一个 div 中,或者把一个 div 放在一个 P 标签中,对吗?所以 HTML 有一个奇怪的限制,叫做短语上下文。
如果您将错误的标签放入另一个标签中,浏览器会决定将其翻转并说我已为您修复了它,好吗?这会导致服务器端渲染的 Javascript 和浏览器解释的 HML 不匹配。这就是,所以我们希望直接在编译器级别实际实现常见的检查。所以当你在做事情的时候,每当你犯了这样的错误,从技术上讲,这不是你的错误。这是浏览器决定处理事情的方式。但是我们会为您检测并警告您,这样您就不必在生产过程中艰难地发现它。
好的,Q3 和 Q4,主要关注点是这些小的次要版本,我们打算做更小更频繁的次要版本,主要研究方向将是蒸汽模式。所以我们已经讨论这个很久了。对于那些对这个术语相对不熟悉的人来说,Vapor Mode 是一种新的编译策略,我们本质上采用相同的 Vue 模板。语法将完全相同,只是编译方式不同。它将具有更高的性能、更有效地使用内存。一般策略是我们将为您的模板生成一个非常静态的基本模板,对吧?所以想象一个普通的模板,通常我们有一堆静态元素和一些分散的动态绑定。因此,我们做到这一点的最有效方法是,如果您要说编写 vanilla js,则必须首先将静态 HTML 放在 HTML 中,然后您将定位那些动态节点,然后在顶部设置一些反应式绑定 的,对吧?。这就是我们希望这种新的编译策略生成的代码类型。有。所以 Vapor Mode 将是一项相对较大的任务。所以它会分阶段发生。
因此,我想向您介绍一下,我们的现状以及我们计划做什么。所以第一阶段是运行时。因此,我们希望本质上拥有所有运行时助手,即支持生成代码的代码,以便能够验证性能影响。这就是我们的处境。我们已经基本完成了这种状态。所以我们已经有了一个能够重现一些核心功能的运行时。所以下一个阶段将是编译器阶段,我们希望能够将 Vue 模板编译成运行时可以使用的东西。然后第三阶段是集成,最后是功能配对。我们将详细介绍每个阶段,因此,现在我们已经完成的第一阶段是支持 VF V4 组件等核心指令,验证性能假设并基于我们的初步基准,它非常有前途,内存效率更高,性能明显更好。然后还有与现有服务器端渲染输出的水合兼容性。所以这非常重要,因为我们希望能够减少我们所做的所有服务器端渲染编译工作。所以这部分不需要改变,但我们就像您在进行服务器端渲染时一样,服务器的 HTML 输出将是相同的,但此 Vapor 模式运行时将能够以相同的方式对其进行水合,所以第一阶段已经基本完成了。
所以我们要进入的下一个阶段是编译器,所以我们想做的一件事就是拥有这种共享代码生成 IR 或中间表示格式。这是 JSX 和模板之间共享的。所以这很重要的原因是因为 Vapor 模式的编译方式实际上意味着你将不再能够用它编写手动渲染函数,因为不再有虚拟 Dom,但是一些用户在使用 JavaScript 时可能仍然需要 JavaScript 的全部灵活性。因此,实际上也可以将 JSX 编译为 Vapor Mode 代码。所以我们的目标是,首先,首先我们将拥有 JSX AST 和模板 AST。这两者都已经存在。然后我们将处理这个中间表示,并将 JSX AST 和模板 AST 转换。为相同的 IR,然后利用该 IR 生成更精细的 Vapor 模式代码。这样您的 JSX 用户和模板用户都可以从 Vapor 模式中受益。第三阶段将是整合,对吧?
所以很多人听到 Vapor 模式时都会问我如何使用它?我必须重写我的应用程序吗?答案显然是否定的。我们的目标是让 Vapor 应用程序可用,可嵌入到您现有的应用程序中,而无需更改现有的应用程序。您可以在组件级别选择加入。您可以开始在应用程序的一部分子集或仅在应用程序的性能关键部分采用 Vapor Mode。显然你也可以,如果你正在开始一个全新的项目,你可以选择以 Vapor 模式运行整个应用程序,这样你就可以删除虚拟 dom 运行时。因此,您的应用程序基础包将更小、更轻。
然后最后阶段将能够反向运行 Vapor 中的 vdom 组件,这样你就可以拥有一个完整的 Vapor 应用程序,但是说我仍然想使用像 Beautify 这样的东西,这需要虚拟 Dom 运行时。我们仍然希望支持这一点。因此,集成阶段是我们预计也必须花费一些时间的部分,因为能够让人们无缝地采用这一新功能非常重要。
最后阶段将是功能对等。因此,Vapor Mode 的初始将仅限于非常核心的必要功能。而一些更多的辅助功能,如 Transition、KeepAlive 和 Teleport、Suspense 将在我们整理完之前的所有目标后在第四阶段实现,这就是 Vapor 模式的计划。
最后,我想提一下我们正在关注的一些未来标准,从法院的角度来看,平台方面的一些新发展可能有利于 Vue 的实施。首先是 Scope CSS。这是 W3C 目前关于 CSS 级联继承标准第六级部分的 W3C 工作草案。已经持续了一段时间了。我们仍然不确定它何时会稳定下来,但是一旦落地,好处是可以极大地简化 Vue 单文件组件中的作用域 CSS。对于那些现在还不知道 Vue 单文件组件作用域 CSS 工作方式的人来说。我们必须做很多工作来为您模拟作用域 CSS,对吗?
当您添加单个作用域属性时,您需要做的就是添加作用域属性。但是 Vue 在底层要做的是,我们必须将范围属性附加到该组件的每个元素上。我们还需要在服务器端渲染期间生成所有正确的 ID。然后我们需要使用 post CSS 来转换 CSS 的每条规则,将这些额外的属性选择器注入到您的选择器中,以便它们仅匹配组件的特定元素,对吗?它在内部非常复杂,我们希望我们可以有一个更简单的实现。这正是本机 @scope 规则允许我们做的。
因此,随着这个新功能的落地,我们现在只需要在每个组件的根部添加类似 scope 的属性即可。我们不再需要将它附加到每个元素上。因此,这首先会提高性能,其次。其次,它将大大减小 html 服务器端渲染 HTML 大小的有效负载大小,对吧?
对。就 CSS 而言,我们不再需要转换组件中的每一条规则。我们只是把你所有的 CSS 都包在这个 @scope 块内。语法所做的是将 @scope 从开始选择器到结束选择器。这意味着在此选择器范围内但在匹配此选择器之前的任何元素。这两个选择器之间的任何内容都将被匹配。那么这里的所有规则都将适用,对吗?
对。所以这在语义上与我们想要的组件范围完全匹配,CSS 平台将为我们提供一种本地描述的方法,这很棒。它将大大简化我们的实现,提高性能。非常期待我们能够迁移到它作为内部实现。我们一直关注的另一个有趣的事情是称为AsyncContext 的第 2 阶段 Echo 脚本提案。
因此,这样做的潜在好处是,当你使用具有异步设置的组件并且其中有等待时,对吗?如果您使用组合 API,你知道在设置内部你需要知道当前实例上下文。因此,当您使用 Vue 核心功能(如计算、监视)及其所有功能时,您可能会遇到一些错误,例如,哦,您在没有当前活动实例的情况下调用此 API。异步操作然后再次调用某些操作时,默认情况下会发生这种情况。因为它是异步的,所以它
实际上丢失了当前的组件上下文。
但 AsyncContext 是一个新的语言功能,它允许我们在异步调用堆栈中保持相同的上下文,而不仅仅是在单个异步调用中,甚至就像当您调用外部或设置超时或类似的东西时,将能够在整个成本堆栈中跟踪相同的上下文。所以这对我们来说非常有用。
现在在脚本设置中,我们实际上又做了很多事情,我们做了很多编译器魔法来为您恢复上下文。所以有了这个,我们将能够在没有黑客的情况下完成它。我们还可以使用它来改进开发工具中的异步操作跟踪。因此,当您有一堆异步操作时,我们可以准确地知道它何时完成并准确地知道它何时改变了您的状态。因此,它也将为您在开发工具中提供更精确的跟踪。
最后一件事是一个新的早期提案,名为 Dom Parts。所以这是非常早期的阶段,但它基本上是Google 一直在研究的一个提案,它很多人受益,不仅可能会使 Vue,比如 Solid、Lit Html, Angular。所以 Angular 也对这样的东西感兴趣。总的来说,这是一种叫做处理指令的新东西,对吧?
它就像 HTML 中充当占位符的特殊语法。因此,当您将这个 HTML 片段实例化为文档片段时,您可以使用一个名为 Getparts 的 API,它将立即为您提供对这些占位符的所有引用,而无需您手动遍历 DOM 树来获取它们。因此,这非常适合进行数据绑定,也非常适合我们要为 Vapor 模式生成的代码。因为我们要做的就是实例化一大块 Dom,并立即获取所有需要绑定的动态节点,然后对它们进行反应式绑定,对吧?所以这是,这将使我们能够在 Vapor 模式下生成更高效、更紧凑的代码。所以这也是我们非常兴奋的事情。所以这几乎就是我们今年剩余时间计划做的事情。有很多事情正在发生,我们希望能够在年底前向您展示 Vapor 模式的工作版本。谢谢你。