为什么响应性语法糖最终被废弃了?尤雨溪也曾经试图让你不用写 .value

你将永远需要在 Vue3 中写 .value

前言

相信有不少新手在初次接触 Vue3 的组合式 API 时都会产生一个疑问:"为什么一定要写 .value ?",一些 Vue3 老玩家也认为到处写 .value 十分不优雅。

那么有没有办法能不用写 .value 呢?有的兄弟,至少曾经有的,那就是响应性语法糖,可惜在 Vue 3.4 之后已经被移除了。

响应性语法糖是如何实现免去 .value 的?这一特性为何最终被废弃了呢?

响应性语法糖

Vue 的响应性语法糖是一个编译时的转换步骤,让我们可以像这样书写代码:

javascript 复制代码
<script setup>
let count = $ref(0)

console.log(count)

function increment() { 
    count++ 
} 
</script> 

<template> 
    <button @click="increment">{{ count }}</button> 
</template>

这里的 $ref() 方法是一个编译时的宏命令 :它不是一个真实的、在运行时会调用的方法,而是用作 Vue 编译器的标记。使用 $ref() 声明的响应式变量可以直接读取与修改,无需 .value

上面例子中 <script> 部分的代码会被编译成下面这样,在代码中自动加上 .value

javascript 复制代码
import { ref } from 'vue'

let count = ref(0)

console.log(count.value)

function increment() {
  count.value++
}

每一个会返回 ref 的响应式 API 都有一个相对应的、以 $ 为前缀的宏函数。包括以下这些 API:

通过 $() 解构

当一个组合式函数返回包含数个 ref 的对象,我们希望解构得到这些 ref,并且在后续使用它们时也不用写 .value 时,可以使用 $() 这个宏:

javascript 复制代码
import { useMouse } from '@vueuse/core'

const { x, y } = $(useMouse())

// x,y 和用 $ref 声明的响应式变量一样,不用写 .value
console.log(x, y)

通过 $$() 防止响应性丢失

假设有一个期望接收一个 ref 对象为参数的函数:

javascript 复制代码
function trackChange(x: Ref<number>) {
  watch(x, (x) => {
    console.log('x 改变了!')
  })
}

let count = $ref(0)
trackChange(count) // 无效!

上面的例子不会正常工作,因为代码被编译成了这样子:

javascript 复制代码
let count = ref(0)
trackChange(count.value)

trackChange 函数期望接收的参数是一个 ref 类型值,而我们传入的 count.value 实际是一个 number 类型。

对于一个使用 $ref() 声明的响应式变量,当我们希望获取到它的原始 ref 值时,可以使用 $$()

我们将上述例子改写为:

diff 复制代码
let count = $ref(0) 
- trackChange(count) 
+ trackChange($$(count))

此时代码可以正常工作,不会再丢失响应性了。

看到这里,聪明的你可能已经意识到了问题:使用响应性语法糖的初衷是为了免去到处写 .value 的麻烦,结果现在新引入了 $ref()$()$(),各自还有不同的使用场景,不是更麻烦了吗?

废弃原因

最终,在收集了大量来自社区的反馈后,经过 Vue 核心团队全员投票,决定移除这一特性。在 Vue 3.3 版本中使用会报 warning,从 3.4 版本开始正式移除。

尤雨溪本人也在 github 上发表了决定将响应性语法糖移除的根本原因,链接:github.com/vuejs/rfcs/...

原文是英语,担心有小伙伴可能看不懂,在这里简单翻译一下:

响应性语法糖的初衷是提供一些简练的语法提升开发体验。我们将它作为实验特性发布并在真实场景的使用中获得反馈。尽管它有一些好处,但我们还是发现了下列问题:

  • 没有 .value 使得难以辨认响应式变量的读取和设置。这个问题在 SFC 中可能不那么明显,但是在大型项目中会造成心智负担的明显增大,尤其是在 SFC 外也使用此语法时。
  • 因为第一条,一些开发者倾向于只在 SFC 中使用响应性语法糖,这就造成了代码的不一致性以及在不同心智模型间切换的成本。这是一个进退两难的窘境:只在 SFC 中使用会造成不一致性,而在 SFC 之外使用则会降低可维护性。
  • 既然总有外部函数会使用原始 ref,那么在响应性语法糖与原始 ref 之间的转换是不可避免的。这就产生了另一个需要学习的东西以及额外的认知负担,并且我们发现这会比纯粹的组合式 API 更让初学者感到困惑。

最重要的是,响应性语法糖会带来代码风格分裂的潜在危险。尽管这一功能是自愿使用的,一些使用者还是强烈反对该提议,因为他们不想维护用了语法糖和没用两种风格的代码。这确实值得担心因为使用响应性语法糖需要的心智模型违背了 JavaScript 的基本语义(对变量赋值会触发响应式副作用)。

在考虑了所有因素之后,我们认为将它发布为一个稳定功能带来的问题会大于收益。

结语

在 Vue3 发布之初,Vue 核心团队就考虑到了 ref 需要到处使用 .value 的繁琐,推出了响应性语法糖试图解决这一问题。

响应性语法糖提供了一系列编译器宏,让开发者在书写代码时不必使用 .value,而是在编译阶段由编译器自动加上。

最终,出于代码风格一致性和可维护性上的考量,这一特性最终在 Vue 3.4 版本被正式废弃。

相关推荐
鱼樱前端1 小时前
今天介绍下最新更新的Vite7
前端·vue.js
coder_pig2 小时前
跟🤡杰哥一起学Flutter (三十四、玩转Flutter手势✋)
前端·flutter·harmonyos
万少2 小时前
01-自然壁纸实战教程-免费开放啦
前端
独立开阀者_FwtCoder2 小时前
【Augment】 Augment技巧之 Rewrite Prompt(重写提示) 有神奇的魔法
前端·javascript·github
yuki_uix2 小时前
AI辅助网页设计:从图片到代码的实践探索
前端
我想说一句2 小时前
事件机制与委托:从冒泡捕获到高效编程的奇妙之旅
前端·javascript
陈随易2 小时前
MoonBit助力前端开发,加密&性能两不误,斐波那契测试提高3-4倍
前端·后端·程序员
小飞悟2 小时前
你以为 React 的事件很简单?错了,它暗藏玄机!
前端·javascript·面试
中微子2 小时前
JavaScript 事件机制:捕获、冒泡与事件委托详解
前端·javascript
Whoisshutiao3 小时前
网安-XSS-pikachu
前端·安全·网络安全