尤雨溪在该 Ref 语法糖的提案中加入了一个新的功能:在单个文件组件(SFC)中引入了新的 script 标签写法,写法为 <script setup> 。这种写法会自动将所有顶级变量声明公开给模板(template)使用。同时在 <script setup> 中引入了一个基于编译器的语法糖,用于消除 ref 的 value 属性,该语法糖在编译期间自动将语法糖转为正常代码。
<script setup>
// imported components are also directly usable in template
import Foo from './Foo.vue'
import { ref } from 'vue'
// write Composition API code just like in a normal setup()
// but no need to manually return everything
const count = ref(0)
const inc = () => { count.value++ }
</script>
<template>
<Foo :count="count" @click="inc" />
</template>
<script setup>
// declaring a variable that compiles to a ref
ref: count = 1
function inc() {
// the variable can be used like a plain value
count++
}
// access the raw ref object by prefixing with $
console.log($count.value)
</script>
<template>
<button @click="inc">{{ count }}</button>
</template>
该语法糖提案发布后,很多开发者在下方留言表示反对。引起开发者们反对的主要原因在于几个方面:对 JavaScript 的魔改太严重;为什么用标签语法而不是直接发明新语法;感觉心智负担变重了等等。部分反对者认为 Vue 此举是想挑战标准,对这种反标准的行为持反对态度。
一位小哥有理有据地对该提案表示了反对:当代码中有两个 ref: 时,就不是合法的 JS 了,所以尤雨溪的提案不合适。
而作者尤雨溪对 GitHub 上的每条质疑都进行了回应。他表示,自己知道这一提案可能会引发争议,但提交该语法糖的动机是希望:
-
通过自动暴露顶级变量来减少代码的冗余度;
-
通过 ref: 语法 让 ref 更高效。
此外,尤雨溪还在知乎上对三个主要的问题进行了回应:
看上去不像 JavaScript
ref: count = 1 使用的是标签语法,在 syntax 层面是合法的 JavaScript,而且在非严格模式下是可以正常执行的,甚至语义也是声明了一个名为 count 的全局变量。同时这也是合法的 TypeScript 语法,不会和类型声明混淆(类型声明必然需要 let 和 const)
当然这里确实只是语法层面的合法,实际上等于是给 ref: 这个标签赋予了一个不同的语义。标签语法本身是一个极少被使用的功能,实际使用也都是用于标记循环声明(用在 for/while 前面),像例子中 ref: count = 1 这样的用法,其原始语义是毫无用处的,这也是为什么我们认为牺牲这个原始语义来获得响应式的变量声明是一个值得的交换。
为什么用标签语法而不是直接发明新语法
使用标签语法确实是受到了 Svelte 的启发。根本原因在于和 JS 保持 syntax 层面的完全兼容能够尽可能保证现有的 JS 工具生态对接。标签语法能够正确地被 Babel,TS parser/transformer(如 esbuild/swc),Prettier,ESLint 以及任何 IDE 的 JavaScript 语法高亮所直接支持,只有在涉及语义的情况下,如类型推导和 ESLint 变量相关规则才需要针对性的兼容。如果用一个全新的非标准语法,就意味着需要在 parser 层面对上述所有工具进行修改,基本不可行。类型推导的问题 RFC 里也有专门讨论,有兴趣的可以看原文,这里不赘述。
感觉心智负担变重了
虽然底层是编译到 ref() 的语法糖,但其实对于新人来说根本不需要知道 ref() 的存在就可以使用,因为在不需要获取底层 ref 对象的场景下,通过 ref: 声明的变量心智模型和用 let 声明的变量的心智模型完全一致。 你就把 ref: 当成一个响应式的 let 就行了。这个模型已经足够实现大部分入门级别的功能,只有到进阶之后开始学习逻辑抽取复用时,才需要知道 ref() 的概念。
对于已经学习了 Composition API 的用户来说会觉得 “又多了一个概念”,同时由于 RFC 事无巨细地讨论了编译的规则,会产生一种 “心智负担增加了” 的错觉。其实我很久以前用 CoffeeScript,Babel,或是刚开始用 TS 的时候,也有这样的感觉,因为我喜欢用之前先看看这东西编译出来是个什么样子。结果就是看过了这个之后用着上层语法,脑子里忍不住去把它转换成底层语法。但这本质上是我们的大脑在习惯了底层思维方式之后的一种惯性。这种惯性在使用新语法一段时间之后很快就消失了,我们的大脑适应能力还是很强的。如果你开始就不 care 编译出来是个什么结果,就更不会有这个问题(你用 nullish coalescing 或者 decorator 的时候会去想着 babel/TS 编译出来是个什么结果么?)
此外,还总结与回答了一些其他的疑问,具体回答可以移步知乎:
https://www.zhihu.com/question/429036806/answer/1564223482
尤雨溪表示在该 RFC 提案发布之前自己就知道会引起很多争议,大部分的反应都在意料之中。他也能理解很多开发者的第一反应都是“不能接受”,不过当初 TypeScript,Hooks,Composition API,甚至是 React/JSX 刚发布的时候也有很多开发者表示不能接受。同时,尤雨溪说:“这只是一个提案!”提案不一定会落地,对问题的回应也并非说服大家该提案一定是“真香”,主要还是为了邀请广大开发者们一起进行探讨与设计。
对于新技术而言,开始的时候大多开发者都会觉得无法接受,但一个技术的优劣一定是需要通过实践来检验的。感兴趣的朋友可以去看看 RFC 原文:
https://github.com/vuejs/rfcs/pull/222
对此你有什么看法呢?欢迎在下方评论区留下你的看法。
活动推荐
开课啦!技术进步使得云服务行业的竞争达到了一个全新的高度,终端“云化”的优点也十分明显,那么普通人又该如何使用 Azure 云呢?12 月 16 日 -12 月 17 日每晚 19:00-22:00 的微软 Azure 基础课程带你上“云”~ 点击下方【阅读原文】立即免费报名上课~
文章评论