查看原文
其他

Vue 3.0 Ref-sugar 提案真的是自寻死路吗?

工业聚 工业聚 2020-11-13

Vue 3.0 的两个提案,最近吸引了许多开发者的注意跟讨论。一个是 script-setup 提案,一个是 ref-sugar 提案。


对于 script-setup 提案,大部分开发者持正面态度。



对于 ref-sugar 提案,却有相当数量的开发者表达否定态度。



在 Github 相关 RFC 里[0],以及知乎问答[1]上,国内外的开发者都积极发表了意见。其中涌现了一些值得咀嚼的看法和现象。


这篇文章将选取几个有趣的片段,加以解读。


一、Vue 提案实际在做什么?


首先,我们先来看一下,Vue 的那两个提案在做什么。


script-setup 提案,是将 options.setup 提取到 top-level。


将下面的写法:


<script>import Foo from './Foo.vue'import { ref } from 'vue'
export default { setup() { const count = ref(1) const inc = () => { count.value++ }
return { Foo, // see note below count, inc } }}</script>
<template> <Foo :count="count" @click="inc" /></template>


用下面这种更简洁形式替代:


<script setup>// imported components are also directly usable in templateimport Foo from './Foo.vue'import { ref } from 'vue'
// write Composition API code just like in a normal setup()// but no need to manually return everythingconst count = ref(0)const inc = () => { count.value++ }</script>
<template> <Foo :count="count" @click="inc" /></template>


而 ref-sugar 提案,则是将 ref.value 的写法,做进一步简化。


<script setup>import Foo from './Foo.vue'
// declaring a variable that compiles to a refref: count = 1const inc = () => { count++ }// access the raw ref object by prefixing with $console.log($count.value)</script>
<template> <Foo :count="count" @click="inc" /></template>


如上所示,代码行数在这两个提案作用下,变得更少更简洁。


这就是 Vue 新提案实际在做的: 在 Vue SFC 文件里,提供语法糖,优化编程体验。


二、批评者认为 Vue 提案在做什么?


我们摘录一些在讨论中,部分开发者对 Vue 提案所作的解读。


1、

请不要再制造更多的 js 方言了

2、

感觉不太靠谱,跟 js 开发者的直觉不一样,容易陷入混乱。

3、

这是一个极其危险的信号


因为修改js语义意味着与生态脱节,而vue的团队说实话还不能自给自足打完一整个生态。


目前来看label语义的修改并不会过多影响,但是会导致开发者对以后vue的生态兼容性产生疑虑从而丢失开发者。


在web的世界里,与标准脱离的,没有一个有好下场,除非倒逼标准,否则没有漏网之鱼。

4、

vue和react都用过一段时间,vue感觉是经常忘记语法,需要对照文档才知道怎么写,react很少需要查阅文档

5、
等过这一套标准越来越复杂,需要重复理解的东西越来越多,并且它并不能给程序员带来深度上的提升,而只是用于写表面的编程的时候,你会发现,这套框架被淘汰了
6、
很不看好,对于动js的语义形成方言这种解决方案应当慎之又慎,为了解决.value的问题是否值得?

7、
别再使用魔法了☹️

8、

想了一整天都没想明白,尤大大会搞出这样子富有争议的rfc。我总觉得使用装饰器会比这个更容易接受…

9、
个人不是很喜欢,违反直觉,不好用。需要增加用户理解成本


三、小改进 vs 大危机?


光从代码形式上看,Vue Ref-sugar 提案属于一个有效的小改进,用更简短精炼的代码,表达相同的逻辑。


然而从概念上,部分批评者却从中解读出了大危机:Vue 企图挑战标准


批评者认为 Ref-sugar 是一个危险信号,意味着 Vue 即将开启一条背离 Web 标准的路线,意味着技术栈押注到 Vue 不再安全,意味着 Vue 背叛了之前的承诺。


对反标准的行为有巨大的反感态度,其实完全可以被理解。


前端工程师这么多年,深受各大浏览器对 Web 标准兼容性不足所累。但凡某个浏览器跟进标准不积极,相比其它浏览器落后许多,就会被戏谑为:XXX is new IE。


我们希望所有浏览器拥抱 Web 标准,我们希望主流框架不要自己搞一套玩法,我们希望在一个坚实的、统一的底层下,专注于产品实现,去解决更有价值的事情,而不是陷入低级问题的兼容处理。


捍卫标准,在前端领域,有着天然的正义性。


因此,批评者认为,Vue 应当在符合标准的前提下,进行改进。


四、标准之下的解决方案们


在这里,我们挑几个开发者们提出的、符合标准的,他们觉得也能解决问题的候选方案。


4.1、问题不存在或不必解决


部分开发者认为,ref.value 不是什么问题,不必解决。


特别的,如果用了 TypeScript 语言,IDE 和编辑器会智能提示,几乎不会出现漏写 .value 的情况,他们也不觉得有什么心智负担。


问题是,并非所有人都使用 TypeScript 编写 Vue 代码,也并非所有开发者都不觉得 .value 是个问题。


4.2、采用 proposal-refs 提案[2]


有开发者搜索出了 JavaScript 里的一个 proposal-refs,并认为这可能是既符合标准,又能帮助 Vue 解决问题的方案。


let ref count = 0;


如果仔细看 proposal-refs 提案,可能发现,它跟 vue-ref 根本不是一回事儿。vue-ref 是 reactive-ref,而 proposal-refs 提案描述的是传统意义上的 Reference Binding。只是都用了 ref 这个词儿。


更何况,这个 proposal-refs 提案,还是一个几年没有动静、没有进展,离成为真正的标准,遥遥无期。无法解决 Vue 眼前的问题。


4.3、采用 proposal-decorators 提案[3]


有开发者又提出,新的 decorators 提案里,支持 Variables Decorators。


即,可以对变量声明进行装饰,放到 vue-ref 场景里,大概像下面这样。


let @ref count = 0


看起来,非常有吸引力,仿佛问题已得到圆满解决。但是:


第一,它太新了,是最近才提出的一个设想,能不能落地,什么时候能落地,完全不可期待。远水救不了近火。


第二,它跟当前几个 decorators 提案的语义和用法不兼容。一旦 vue 采用新版装饰符提案,很可能意味着,当前被大量使用的、主流风格的 decorators 用法,不能用,有冲突。


第三,它只解决了 vue-ref 的 create 部分的问题,没有解决 access 和 update 部分的问题。


什么意思?


let @ref count = 0
// 访问必须通过 count.valuecount.value
// 赋值必须通过 count.value=count.value += 1


装饰符只是帮我们创建了一个 ref,但访问 ref.value 和更新 ref.value 部分是缺失的。


将 proposal-refs 跟 proposal-decorators 结合起来,倒可以克服上述困难。变成下面这种写法:


let @reactive ref count = 0


顺序很重要,先变成 ref 对象,再添加 reactive 能力;如果是下面这种,恐怕不行:


let ref @reactive count = 0


它变成 ref 了一个 { value: 0 },后续还得带着 .value。


可惜的是,上述设想,是在两个落地概率低迷的提案下,叠加起来更加低迷的情况。很难有所期待。


4.4、变量命名约定方案


有开发者提出,可以约定 ref 变量名,去标记一个变量属于 vue-ref。


let countREF = 0
console.log('count', countREF)
countREF += 1


或者简化 .value 属性。


let count = ref(0)
console.log('count', count.$)
count.$ += 1


尽管没有彻底解决问题,但他们认为这样减轻了问题。


从实践上,这种做法或许可行,但这种解决水平,可能不值得。开发者依然被时刻提醒,所操作的变量在一个 ref 包装中。


4.5、comment-based 方案


有开发者提出,可以通过 comment 注释,去标记一个变量属于 vue-ref。


// @reflet count = 0
console.log('count', count)
count += 1


这种做法不无道理,JSDoc 就用注释的方式,去做函数类型标记和文档描述,并取得了成功。


然而,vue-ref 的问题,还有另一面:访问 ref 对象。我们无法 watch 一个 Primitive value,我们需要 watch 一个 Reactive value。


因此,comment-based 方案,即便放弃了 ref: 标签语法糖的部分,可能也需要保留 Ref-sugar 的另一个特性:$count。


// @reflet count = 0
watch($count, () => {})


也就是说,对于解决 vue-ref 问题来说,comment-based 自身是一个不完整的处理。也会带来新问题,开发者可能开始频繁忘记加注释标记,需要建立先写注释再写变量声明的新习惯,也是一大挑战。


还有其它一些方案未被提及,它们大多更不能打。


总的来说,这些标准之下的其它选择,都有各种各样的缺陷,并不能充分解决 vue-ref 的问题。


五、标准的构成和演化


回想一下,那些批评 ref-sugar 违背规范和标准的表述。


标准一词,主要起到不容置疑、不容违背、不容商榷的作用,不是一个清晰的面貌。


大家将标准和规范,想象得太完美了。


其实标准和规范,并非铁板一块,并非处处不可妥协。如果我们愿意凑近标准和规范,我们会看到更多细节……


5.1、标准和规范里存在落后于时代的部分


标准和规范,也起到一个记录和向后兼容的作用。特别是像 JavaScript 这类先事实上存在,后面才去撰写标准的事物。


在 ECMAScript 规范里,不乏对 JavaScript 老旧的、落后于时代的、不再被推荐使用的语言特性的整理和描述。如 arguments, with, prototype 等。

因此,即便符合标准和规范,也不一定是好的、正确的、符合时代需求的。


5.2、标准和规范里,不是所有东西都一样重要


Vue Ref-sugar 提案,其实并未修改 JS 语法,只是赋予 labeled statement 新的语义,用以声明一个 vue-ref 对象。因此 Vue 作者尤小右表示,它仍然是合乎语法的 JS 代码。


然后,有开发者从 ECMAScript 规范里[4],找到了这一句:


15.1.1 Static Semantics: Early Errors


It is a Syntax Error if ContainsDuplicateLabels of StatementList with argument is true.


在相同作用域内,出现重名的 labels,在 Static Semantics 层面上,属于 Syntax Error,即语法错误。


部分开发者因此认为,这有力地驳倒了尤小右声称的合法 JS 代码,这坐实了 Vue Ref-sugar 不仅从语义上违背规范,语法上也破产了,属于彻头彻尾地反规范事物,应当放弃。


这个看法,可能也不太能站得住脚。


因为,Static Semantics 也是语义的一种[5],只是它的一些规则,恰好描述的是语法结构。当尤小右说 ref-sugar 依然是合法 JS 代码时,他说的合法,有严格的特指,就是 Parser 层面的。他不曾试图表达过任何 Static Semantics 等其它层面的合法性。


他一直强调的是,Babel、Webpack、TypeScript、ESLint、Prettiter、V8 等工具和运行时层面对 JS 代码解析上的合法性。


我们不能驳倒一个人没发表过的意见。


更重要的是,经过测试和验证,Babel、Webpack、TypeScript、ESLint、Prettiter、V8 等工具,全部没有实现 ContainsDuplicateLabels 的 Static Semantics 特性。



如上,在 chrome 控制台里,运行包含重名 labels 的代码,并无任何问题。


也就是说,事实上,从实践层面,并非标准和规范里所有部分,都有同等的重要性。一些无伤大雅的部分,许多工具和 JS 引擎,并没有实现。


因此,并非标准和规范里的所有部分,都值得我们采取一样强硬的态度。


5.3、标准和规范是演化的、语义是可以调整的


批评者说 ref-sugar 修改了 labeled statement 的语义,属于大逆不道。


然而事实上,在某个上下文里,调整原有语法的语义,是非常自然的、常见的、可行的做法。


JavaScript 语言自己也干这事儿——严格模式。


JS 代码在严格模式和非严格模式下,相同语法,也有不同语义行为。


未声明就做变量赋值,在非严格模式下,相当于给全局变量赋值;但在严格模式下,则抛出错误。


同一段代码,ES3 和 ES5 两个标准产生了不同的语义解读。


标准不是一成不变的,标准也在发展,在演化。


当然,批评者或许继续坚持:ES5 相对 ES3 是新的语言标准,只有新的语言标准可以做出调整,而 Vue 只是 JS 语言里的其中一个框架,它不能这样做。


那么,我们来看下面这段代码:


const http = require('http');
const hostname = '127.0.0.1';const port = 3000;
const server = http.createServer((req, res) => { res.statusCode = 200; res.setHeader('Content-Type', 'text/plain'); res.end('Hello World');});
server.listen(port, hostname, () => { console.log(`Server running at http://${hostname}:${port}/`);});


熟悉 Node.js 的开发者,立刻识别到,上面不就是一段 node.js 的 hello world 示例代码吗?跟我们现在讨论的问题,有什么关系?


从功能角度去理解的话,确实如此。如果我们从 JS 语法和语义角度去看,情况就不同了。


很容易发现,从 JS 代码原始语义来看,上面的代码,应该产生 4 个全局变量:http, hostname, port, server,因为它们在 global scope 里被声明和使用。


然而,事实上它运行在 node.js 里,并不会产生上述全局变量,它们会被包裹在一个函数里执行,它们是一个 commonjs module,而不是朴素的 JS 代码。


也就是说,这段 JS 代码,在 commonjs module 这个上下文里,它的语义行为被改变了,从挂载全局变量,变成 commonjs module 里的局部变量。


node.js 是一个 runtime/platform,它不是 ES5 性质的语言规范,它也在修改 JS 代码的语义/行为。

那么,ref: count = 1 这句 JS 代码,在 vuejs SFC 这个上下文里,它的语义行为也发生一些调整,从性质上,跟 ES5 和 Node.js 上下文里做调整,又有什么本质区别呢?


调整代码的语义行为这事儿,ES5 新语言规范做得,Node.js 新平台做得,Vue.js 新框架就做不得吗?


批评者或许继续缩小它的抨击范围:Vue.js 自然是可以调整一些语义行为,script-setup 提案就调整了 top-level 的输出,将它输出到了 options.setup 里,大部分开发者都表示认可。但不管 ES5、Node.js 还是 script-setup,都是在原有语义下的小调整,要么报错,要么从全局变量变成局部变量。


但是,ref-sugar 是将 labeled statement 赋予了跟它之前语义毫不相干的新语义,这是难以接受的。


5.4、标准和规范往往滞后于实践


假设开发者们,严格地在标准之下行事,技术发展可能陷入迟缓,甚至是停滞。


标准和规范,常常是滞后于实践。先有了实践层面的各色探索,形成了很多形式上不尽相同、但本质上差异不大的多种解决方案后,产生了标准化的需求。


Babel 工具的一个用途就是,先让开发者在实践中,去编写还没完全进入到标准里的新语言特性,根据开发者实际使用的反馈,调整该 proposal 的 stage,最终落地到标准里。


也就是说,作为前端工程师的我们,本来就被允许使用一些非当前稳定规范下的技术特性。


如果大家觉得,Babel 推进 proposal 起码是一条走向规范的路线,而不是像 Vue 的 Ref-sugar 那样,可能走向规范的反面。那么:


TypeScript 语言的存在,它为 JavaScript 插上了 Type-System 的翅膀,而 ECMAScript 规范,对 Typed-JS 并没有任何表示要纳入的意思。


这意味着,使用 TypeScript 也不是一条走向 ECMAScript 规范的路线。甚至 TypeScript 自身,目前为止也没有规范。


那这意味着,前端工程师们,使用 TypeScript 也是大逆不道、违背 ECMAScript 发展路线的事情吗?


标准和规范,不一定提供了我们想要的全部工具,有的它还没提供,但将来会提供,同时也需要我们配合使用不稳定的标准特性;有的它不会提供,我们需要寻求其它途径和工具,去代为满足。


这种超前的实践探索,不仅仅发生在 Babel/TypeScript 等语言层面,在前端框架层面,也存在。


React 的 JSX 语言拓展,就不属于 ECMAScript 规范的范畴。但不妨碍它当前流行于前端开发中,带来巨大价值。


React Team 成员 Sebastian 在 2016 年,曾经提了一个 one-shot Delimited Continuations with Effect Handlers 提案[6],他认为这个特性,对于 UI Framework 很有价值,希望能推动它的进展。但最后,提案不了了之。


React Team 并没有停下来等待标准,而是选择重构 React 底层,采用 Fiber Architecture,由框架接管组件的 call-stacks 管理,通过各种技术手段,模拟 Algebraic-Effects 特性。后续推出了 React-Hooks 新特性,对 Vue 等其它框架的发展,也起到了正面影响。


标准再一次滞后于实践,并将继续滞后。


技术发展,需要超前实践探索,我们既要允许 Node.js 这类新平台,TypeScript 这类新语言,Babel/JSX 这类新工具提供一些新用法,也应允许 React,Vue 等新框架或框架的新版本,做出它们的新探索。


正是这些新探索,帮助规范指明了方向。知道哪些模式,已经被证明是极有价值的。


因此,Vue SFC 里对 labelled statement 语法,做一些有效的、安全的语义挖掘,并没有大家想象得那样反常。


5.5、规范不是唯一权威来源


如果我们将视野继续打开,跳出 JavaScript 层面。我们可以会发现,ECMAScript 规范,只不过是其中一门语言里的规范。


这个世界上,还存在很多语言,它们也有自己的优秀之处。它们值得 JavaScript 学习。事实上,JavaScript 已经向其它语言,学习到很多东西了。


那么,在其它语言里,Vue Ref-sugar 这种性质的做法,也是如此大逆不道的吗?


不是的。


比如,$count 这种约定式变量,在 Swift/Kotlin 等语言里也存在。


struct AlternativeRect { var origin = Point() var size = Size() var center: Point { get { let centerX = origin.x + (size.width / 2) let centerY = origin.y + (size.height / 2) return Point(x: centerX, y: centerY) } set { origin.x = newValue.x - (size.width / 2) origin.y = newValue.y - (size.height / 2) } }}


如上,Swift 的 computed property 支持 Shorthand Setter Declaration 写法[7],即可以省略 set(value) {},用约定式变量 newValue 访问 setter 参数。


尽管 newValue 只有一个变量,而 Vue Ref-sugar 根据 ref: 的次数,可以创建很多 $ref。但这只是数量上的差别,从性质上,它们是一样的。


{-# LANGUAGE TemplateHaskell #-}{-# LANGUAGE NoMonomorphismRestriction #-}{-# LANGUAGE FlexibleContexts #-}{-# LANGUAGE MultiWayIf #-}module ShellCheck.Parser (parseScript, runTests) where


如上,Haskell 支持通过 {-# LANGUAGE * #-} 的语法,添加语言特性插件。


甚至,有的支持 Macro 的语言,如 Rust,它们本来就提供了改变语法和语义的标准工具。


let value = json!({ "code": code, "success": code == 200, "payload": { features[0]: features[1] }});


如上,rust 的 serde_json 提供了 json! macro,让开发者可以在 rust 代码里编写 json。


let mut doc: DOMTree<String> = html!( <html> <head> <title>"Hello Kitty"</title> <meta name=Metadata::Author content="Not Sanrio Co., Ltd"/> </head> <body> <h1>"Hello Kitty"</h1> <p class="official"> "She is not a cat. She is a human girl." </p> { (0..3).map(|_| html!( <p class="emphasis"> "Her name is Kitty White." </p> )) } <p class="citation-needed"> "We still don't know how she eats." </p> </body> </html>);let doc_str = doc.to_string();


如上,rust 的 typed_html 提供了 html! macro,让开发者可以在 rust 代码里,编写 html 标签结构。


#[derive(juniper::GraphQLEnum)]enum Episode { NewHope, Empire, Jedi,}


如上,rust 的 juniper 提供了 Derive macros,可以标记一个 rust enum 对应了一个 GraphQL Enum。


这种做法,类似于前面提过的 comment-based 方案。但不管是 comment-based 还是 label-based,只是形式不同,重要的是,它们都是通过标记的方式,修改或者增强特定语法的语义。


我们不能因为当前 ECMAScript 没有提供类似 Language Extension 或者 Macro 的功能,就放弃学习在其它语言里,业已被证明可以带来正面价值的做法。


当前主流前端开发基础设施里,其实已经带了多个 Parser 和 Compiler,如前面列举的 Babel、Webpack、TypeScript、ESLint、Prettiter、V8 等,Vue 也有自己的 vue-compiler。


我们的代码,几乎都通过多次编译处理。从工具链的能力上,完全可以提供类似 language extension 和 macro 性质的转换。babel plugin,babel macro, angualr-compiler, vue-compiler 等,已经对一些 template 事物,进行过这类处理。


其实迟早有一天,前端框架会逐渐榨干当前 JavaScript 语言的表达能力,继续深挖,也很难有效解决问题。需要动用 compiler 的处理。这正是 Svelte 在努力的方向,它充分发挥了 compiler 的能动性,不通过 runtime 抽象,通过 compile-time 转换,实现 reactivity 机制。


React 在 runtime 抽象上深耕已久,也实现了 Concurrent Mode 这种复杂的调度处理。但依然在探索通过 compiler 去自动补充 useEffect 依赖数组的可行性。


Angular 的 next-generation compilation and rendering pipeline 技术——Ivy,也是通过编译技术去优化。


早在几年前,社区就喊出了 Compilers are the New Frameworks 的声音。


Vue 不是第一个迈出这一步的。相反,在主流框架里,Vue 可能是最谨慎的那一个,它选择 labeled statement 语法,正是受到 Svelte 里相似用法的启发。已经是在前人探索的成果下,所作的相对成熟的考量。


六、Label-sugar 提案


Vue 的谨慎之处也体现在,其实 Ref-sugar 是更普适的 Label-sugar 的特例。


Vue team 深深地考虑过大家对新语义可能的排斥,决心用最小的、最低成本的牺牲,去解决 vue-ref 的易用性问题。


在此,我们可以做个头脑风暴,看看更普适的 Label-sugar 是怎样的。


比如,提供更多不同作用的 label。



如上,用 expose: 来表达组件对外的输出,用 mounted: 表达 onMounted,用 unmounted: 表达 onUnmounted。


label 可以组合起来,expose: ref: 表达对外输出一个 ref。


再比如,用在 React 组件里:



如上,我们用 state: 表达 useState,用 effect: 表达 useEffect,用 callback: 表达 useCallback。组件代码写起来,更加规整和简洁。


这是一个有趣的设想,我个人并不排斥这种风格。在我看来,它相当于对 commonjs module 这种性质的处理的细化,从 module level,细化到 function level。在函数里,通过 label 增强函数表达能力。


可能我们一时半会儿,走不到上面那一步。Vue 在 Ref-sugar 里,前进一小步,验证一下,也算一个好事。


七、总结


以上,这篇文章,并不是想说 Vue Ref-sugar 一定是正确的路子,我们要无条件支持它。


而是想强调,Vue Ref-sugar 的做法和设计,并没有部分批评者描述的那么可怕和危险。它看起来,更多的是水到渠成的一小步。这种做法和设计,在其它语言和工程里,也不鲜见。即便是在前端领域,Vue 也不是第一个这样做的。


Vue Ref-sugar 是 opt-in 的,既没有强制开发者使用它,也没有阻碍开发者使用他们喜欢的、熟悉的模式。


如果在标准之下,能找到满足要求的解决方案,自然是最好,不必冒险探索。


但是,在找到那种完美方案之前,我们也不能放弃眼前可以把握到的优化,停下来,无所作为。


即便 Vue Ref-sugar 最终被证明是错误的路线,从它的影响范围和处理方式来看,也不是不可回头的。可以很容易通过 codemod,批量的、自动的将所有 Vue Ref-sugar 代码,转换成原始形态,或者新发掘的形态。


同时,我们也应该更加包容,允许框架在一定程度上犯错。很难奢望永不犯错的完美框架,但是我们可以期待一个会自我纠错的成长型框架。


对于这样一个可选的、可撤销的、谨慎的、有前人成功探索经验的提案,我没有想到会被如此激烈反对和批评。


我感觉,根源上,可能是大家对语言标准和框架之间的关系的理解,出现了差异。


或许在很多开发者心里,框架 <= 语言。


比如,Vue, React,Angular 和 Svelte 是 JS 语言里的几个框架,它们属于 JS 生态的子集,它们在 JS 范畴的约束下。


Vue Ref-sugar 在这个角度,就成了挑战权威,自寻死路。


这种观点,不能说完全错误,但却是比较片面的。


框架其实比我们想象的定义更大,大到:框架 > 语言。


框架面向的是特定领域的解决方案,所有语言和工具,都是它可以选择的手段。


有时,它选择某一门语言作为主要工具,甚至所有相关软件都用同一门语言去实现。但这不意味着,这门语言就凌驾于框架之上。


放到 Vue 角度来说,JS 只是它的一个 runtime 语言选型,实际上,Vue 3.0 选用的编写语言,已经是 TypeScript。将来,Vue 可以由其它语言编写,通过 compile-to-js 或者 WASM 等方式,运行在 Web/Node.js 平台,以及运行在其它 Native 平台。


Vue 里也使用了 HTML,CSS 以及自定义的 Template 等语法。Web 开发本身就是一个多门语言协作的模式,任意一门语言都很难说凌驾于谁之上。


在其中,JavaScript 只是其中一门语言罢了。当 JS 可以很好地解决问题时,Vue 选择它编写代码。当 TS 能更好地解决问题时,Vue 选择 TS。


当 JS 原始语义可以很好地解决问题时,Vue 选择保持原始语义。当 JS 增强语义可以很好地解决问题时,Vue 选择增强 labeled statement 语句的语义。


Vue 面对的是 UI 开发的领域问题,它不是 JS 的附属,反而 JS 是它用以解决领域问题的其中一个工具和手段。


Vue Ref-sugar 提案,在这个角度下,就成了一个自然而然的事情。


为了解决问题,包括 JS/TS/Template 等一切技术和工具在内,都是可调整的。关键要看,成本和收益。


只要收益从长期来看,大于甚至远大于成本,那这个方案就是可用的。


如果批评者愿意仔细阅读尤小右的解释和回复,从成本-收益角度去理解,很容易发现,选择 label statement 是一个充分考虑了工具链适配成本、学习成本、使用成本等多方面的务实选择。


框架作者,比写标准和规范的人,离开发者更近,更关心开发者的诉求。


开发者的诉求,先被框架作者所吸纳,而后写标准和规范的人,还要去跟框架作者们沟通与合作,提供可以满足框架后续发展需求的新特性可能。


框架迫切想得到的特性,甚至还会直接贡献代码给上游(如 Facebook 给 Google Chrome 贡献代码,实现了 React 框架需要的 isInputPending api)。


选择相信有诚意的、有责任心的框架作者们,比盲目崇拜标准和规范,对我们来说,更加有意义。


现在,再去看一遍 Vue Ref-sugar 提案,不知道是否有不同体验呢:——)


[0] New script setup and ref sugar

https://github.com/vuejs/rfcs/pull/222


[1] 如何评价 Vue 的 ref 语法糖提案?

https://www.zhihu.com/question/429036806


[2] proposal-refs

https://github.com/rbuckton/proposal-refs


[3] Decorators: A New Proposal

https://slides.com/pzuraq/decorators-a-new-proposal-2020-09


[4] ECMAScript® 2021 Language Specification

https://tc39.es/ecma262/#sec-scripts-static-semantics-early-errors


[5] Wiki: Static Semantics

https://en.wikipedia.org/wiki/Programming_language#Static_semantics


[6] One-shot Delimited Continuations with Effect Handlers

https://esdiscuss.org/topic/one-shot-delimited-continuations-with-effect-handlers


[7] Swift: Shorthand Setter Declaration

https://docs.swift.org/swift-book/LanguageGuide/Properties.html

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存