360 高级前端架构师 Hax(贺师俊):前端开发编程语言的过去、现在和未来

阅读数:5014 2019 年 7 月 2 日 17:31

360高级前端架构师Hax(贺师俊):前端开发编程语言的过去、现在和未来

在日前的 GMTC 全球大前端技术大会上,360 高级前端架构师贺师俊发表了《前端开发编程语言的过去、现在和未来》的演讲,本文整理内容如下。

对待编程语言的态度

关于编程语言我首先想讲一个事情,就是讨论编程语言时,我们经常会看到两种非常极端的态度:

  • 一种就是“XXX 语言最好!”XXX 可以是任何一个语言,比如 PHP🤪。如果讨论的时候你不附和他的观点,就可能给你一个鄙视脸。

  • 另外一种,就是“所有语言都一样!”当我们讨论不同语言的特性优劣,他也会给你鄙视脸。

虽然这两种态度看似截然相反,但这两种态度的结果是一样的,就是讨论继续不下去了,所以你看各种技术群里涉及编程语言的讨论往往要么曲高和寡,要么迅速以“PHP 是最好的语言”或者“语言都一样”这样毫无意义的废话(按照复读机队形)收场。排除调侃和灌水的成分,本质上,这两种极端态度背后的原因是类似的——人们总是高估短期,低估长期。

编程语言,以我的看法,不太可能是项目成败的决定性的因素。但是长期来看,编程语言,作为程序员创造软件的核心工具,会影响团队长期的生产力。这是我们为什么应该摒弃那些极端态度,投入一定的精力去研究和发展编程语言。

前端语言的特点

GMTC 是大前端技术大会,所以我们这次讨论前端和移动端开发中的编程语言。以下简称前端语言。

前端语言有自己的特点,也就是和一般非前端领域的编程语言有区别。如果我们观察前端语言,会发现有两个特点:

前端语言与平台高度相关

什么叫平台高度相关呢?比如说今天我们要在 iOS 平台上开发新应用,那基本上就得用 Swift 语言。反过来说,你要在其他平台上开发新应用,就不太可能选择 Swift。

相比较而言,后端开发就没有这么强的约束,因为后端服务并不是说非用哪个平台,比如 Java、.NET 不可,完全可以用 PHP、Go、Python、Ruby 等各种语言写,百花齐放。就算用 Java 或.NET 平台,也有多种语言可选。而前端开发中,语言和平台具有强相关性,整个技术栈的选型中,选平台和选语言是一而二二而一之事。

这会造成一些困扰。比如说因为你看到了 Flutter 这个平台非常好,所以你就用它,可你也一并选择了 Dart 语言,但是很有可能你其实不是特别喜欢 Dart 语言——尤其如果我们已经很依赖 Kotlin/Swift 语言所提供的某些高级语言特性时。

也有另外一种情况,比如,假设你的团队一直使用 JavaScript/TypeScript,并对整个 JS/TS 生态里已经有很大的投资(如积累了大量库,自行研发了许多工具等),所以不想放弃这样一个技术栈,那在这样的情况下,很遗憾你现在就没有办法选择 Flutter 平台。所以我们会发现,在前端开发过程当中就有这个技术栈选型上的约束:编程语言跟平台高度相关。

前端语言与领域高度重合

如果我们讲前端语言,不管哪一个语言,它其实都是以前端应用某特定领域为首要目标,所以问题领域是高度重合的,面临的挑战也是共通的。

比如都要处理界面和交互。许多其他编程语言就不一样,它们各自有一些独特的特点,擅长不同的领域。比如说 Erlang 就很适合用来开发分布式系统,它本来的设计目标就是针对这种场景,并且有针对性的、独特的并发模型和容错机制等。

再比如说像大数据、人工智能领域就首选 Python。而 Go 就常见于网络服务层、云设施。甚至像 Coq 这样偏学术的语言,因为其具有形式证明能力,被运用于金融、区块链等领域。所以虽然都是通用编程语言,但不同语言可能有非常鲜明的特点,主攻领域有很大不同。而前端语言,虽然各平台有各自的语言,但其主攻领域其实是高度重合的。

前端语言的过去

从这两个特点伸发开去,我们来看看前端语言的过去、现在和未来。其实这个标题有点大,这里只能把个人在这方面的观察和思考跟大家分享一下,希望能引发大家的思考。

先看一下过去。这里讲的“过去”,是比较近的过去,更早的就不讲了,比如最早移动开发是用 J2ME,这个就太古早了。这里我们还是以智能手机为起点,也就是 Android、iOS 时代。

过去三个主要平台的语言,在 Web 就是 JavaScript(下简称 JS),Android 就是 Java,iOS 就是 Objective-C(下简称 OC)。

为什么前端很少用其他编程语言

那么是不是可以用其他编程语言呢?

在 Web 平台确实还有不少其他语言,各种 altJS(可编译到 JS 的其他替代性语言),以前一度 CoffeeScript 也挺流行。但是从整个发展来看,虽然我们总是可以用其他语言编译到 JS,然而随着 ES6 的兴起,总体趋势还是重新回归了 JavaScript。

Android 用 Java,理论上 Java 平台上的其他语言也可以用,比如 Groovy、Scala、Clojure 等,但是这样一种实践,也不是特别流行。

为什么呢?原因可能各种各样。比方说工具链不匹配。像构建,Scala 用 sbt 而 Android 用 Gradle。不是说不能解决,但需要花费开发者额外的精力。还有些问题可能就是无解的。比如,当你用另外一个语言,它通常有自己的标准库。对于前端和移动端来讲,你发布的应用,用户要通过网络下载,就有一个大小的问题。如果你使用其他语言,虽然可以编译过去,但是还要带上他的库,就使整个包变大。额外的 runtime 库造成发布包变大,对后端来说倒是无所谓,前端就很敏感。尤其是一些大公司的 app 非常复杂,由多个团队共同开发,其包的大小限额也是要在多个团队里分配的,即使其中一个团队认为引入新语言有极大的好处,但你怎么能说服所有团队为业务代码以外的代价买单?非常困难。

为什么 CoffeeScript 能一度流行,很大原因是它选择了聚焦于语法改进,不需要任何额外 runtime 库的路线。然而这也限制了它的发展,当 ES6 也很大地提升了语法便利性之后,CoffeeScript 的价值就大大下降,走向衰落。

实际上 Swift 也有需要打入 runtime 库导致应用变大的问题,但是它是 Apple 的亲儿子,所以大家确信这只是暂时的问题,我们看到最新的 Swift 5 就解决了这个问题,新版的 iOS 操作系统已经内置了库。但是想象一下,如果是其他语言呢?

所以总体来讲,虽然存在使用其他编程语言的这个可能,但是在实践上,无论 Web、Android 还是 iOS 平台,整个前端来说,我们在真正 production 里还是比较少采用其他的语言。
但是呢,这些上一代的前端语言确实是存在一些问题的。

JS 的问题

什么问题呢?我们先讲 JS 的问题。主要就是:不适合 PITL。

PITL 就是 programming in the large,大型编程。所谓大型编程,时间和组织两个维度,一个是这个软件或服务有比较长的生命周期,另一个就是这个产品或项目有比较大的团队需要多人协作,满足任何一条,就可以算大型编程。

JS 这个语言当年设计的时候,不是为大型编程这个目标设计的,它的初衷,就是为满足在页面上做一点动态效果的需求,所以 JavaScript 名副其实是一个 script(脚本)语言。所谓脚本,很多时候就是一次性书写甚至一次性运行,用后即抛。传统上,脚本语言的设计目标就是方便使用,怎么易用怎么来,所以在很多方面并不适合大型编程,甚至背道而驰。比如说大家都知道 JS 的弱类型隐式转换问题,像臭名昭著的“三位一体”,虽说是吐槽,但实践上也确实会造成隐患,所以当团队要进行“大型编程”,就必须用 linter 等工具去给语言的设计缺陷擦屁股。

Java 的问题

Java 又是什么问题呢?其实 Java 本身是非常好的语言,到目前为止仍然是我们整个行业最重要的编程语言。

Java 的问题就是 Oracle。Oracle 的各种槽点就不说了,在 Android 平台来说,最主要的就是和 Google 的官司。这个是非技术的因素。

技术的因素也有。Java 虽好,但有一个问题:尽管 Java 最初也是“前端语言”(早期设计目标是用于嵌入式设备和 Web),但 Java 最成功的领域是在服务器端。那么这个语言的演化就要考虑服务器端,不能仅仅因为前端和移动端有一些新的需求,就把一个语言特性加进去,而是要考虑更广泛的场景来衡量这样一个特性是不是值得加进去。

而我们知道,服务器端跟前端、移动端还是有一些差别,比如说平台更新迭代的速率和普遍性,对 backward compatibility 的要求,对性能如内存占用、启动时间的需求,等等。这种不同会不会对社区心态产生不同的影响从而对语言的演化也产生影响?这或许是一个可以思考的问题。

OC 的问题

Objective-C 是非常独特和有趣的语言,它有一个问题是和其他主流工业语言存在一定的 gap。最简单的例子,我们看 OC 代码的命名风格,都是非常的长,比其他语言长得多。为什么那么长呢?一个原因是其代码风格就鼓励自说明的、接近英文自然语言表达的、完整到有时觉得冗长的名字。另外一个是,OC 中,参数名也是命名的一部分,方法名字和后续参数名合在一起构成整个的命名。最终效果就是在其他语言中字符串替换方法名就是一个简单的“replace”,而在 OC 中则是“stringByReplacingOccurrencesOfString:withString:”。加之 OC 的词法比如括号运用也和其他语言不太一样,完整一个语句大概长这样:

复制代码
newString = [oldstring stringByReplacingOccurrencesOfString:@"oc" withString:@"swift"];

虽然说,坊间流行“Java 和 JavaScript 的关系是雷锋和雷峰塔的关系”,但 Java 和 JS 的切换其实并没有那么难,至少语法是很接近的。但是从 Java、JS 这样的主流工业语言切换到 Objective-C 就障碍比较大,光语法和命名这些比较表面性的东西就已经没那么容易适应了。

当然 OC 还有其他的问题,像性能、内存安全性等。但我个人认为 OC 和其他主流语言的过大差异或许是最大的问题。

前端语言的现在

我们看一下现在前端编程语言的状况,可以看到很明显的变革。在 Web 平台,大量新的应用全线转用 TypeScript,还在维护的老项目迁移到 TypeScript 的案例也不在少数,尤其是在大厂,这个趋势更加明显。在 Android 平台,则是 Kotlin,最近的统计是超过 50% 的 Android 程序员已经切换到了 Kotlin。而在 iOS,当然就是 Swift。除此之外,我们还有了一个新的平台,就是 Flutter,它引入了一个“新”语言:Dart。

我们发现,所有的端,这些平台,都在比较短的一段时间内,从老的语言进化到新一代的语言。这不知道是不是巧合(实际上巧合背后往往是有原因的)。

前端语言平台钦定

这些新语言,与一般的编程语言有什么特别的不同呢?首先,我们之前已经提到过平台和语言的关系。你会感觉到前端的编程语言似乎都被平台所“钦定”。

比如 Kotlin 就是 Google 宣布作为首要支持的语言。Swift 是 Apple 主推的。TypeScript 虽然说微软没法“钦定”它,但 TypeScript 本身是 JS 和类型系统的结合,而 JS 是 Web 平台唯一语言,让 TypeScript 也附带获得了“钦定”。TypeScript 在享受 JS 的红利的同时,也通过更好的工具支持反哺 JS 生态。整个 JS 生态正逐步进化为“JS/TS”生态。

最后还有 Dart,这个语言其实已经诞生好几年了,在 Web 和 Android 平台上寻求发展接连受挫之后,终于获得了 Flutter 平台“钦定”从而起死回生。总之,前端各平台上所有这些新语言和上一代语言一样,我们感觉都是“钦定”。

编程语言的跨端和全栈

虽然是平台“钦定”,但是我们可以发现,所有这些语言他们都不满足于被“钦定”的那一亩三分地,它们都在走向跨端和全栈,突破单一平台。

像 JS/TS,Node.js 在全栈开发上已经很多年,很成熟了。跨端方面,则有 Cordova、React Native、Weex、NativeScript 等各种跨端方案。再说 Dart,其实 Flutter“钦定”了它必须能跨端。Dart 既有 standalone VM,又可编译到 JS 和 Native。基于 native,可以跑在 Android 和 iOS 上,基于 dart2js 则有 Flutter for Web,standalone VM 则给予其全栈的潜力。

Kotlin 语言在语言设计之初也设定了多 target 的目标,可以编译到 JVM、JS 和 Native,所以无论跨端还是全栈也都没有问题。相对来讲,Swift 在跨端方面较弱,尽管技术上说 Swift 经过 LLVM 编译到 native,你当然也是可以用 Swift 写 Android 应用的,社区也有这样的尝试,但 Apple 对此缺乏动力,不太可能像 Dart 和 Kotlin 那样有官方支持。但是全栈道路是没有障碍的,Apple 还建立了 SSWG(Swift Server Work Group)来推进。此外 Swift 也可能会官方支持编译到 WASM 来支持 Web 平台。总之,各语言都在不断发展其跨端和全栈的能力。

JS/TS 仍然是跨端和全栈能力最强的语言

这些语言当中,总体来看,JS/TS 仍然是跨端和全栈能力最强的语言。Web 自不必说,Android、iOS 系统也都是带有完整的 JS 引擎的,至少语言本身的标准库是无需额外成本的。也因此 JS 本身就是一个编译 target。基本上现在所有的新语言都把能够编译到 JS 作为必须达成的目标。

如果一定要说 JS 的弱点的话,可能是缺乏编译到 Native 的能力。虽然 JS 引擎高度优化,但在极致性能上仍然没法跟 Native 比。但我们现在有 WebAssembly!JS 引擎同时也是 WASM 引擎。当我们发现有性能瓶颈,就可以使用其他语言如 C、C++、Rust 等编译到 WebAssembly 从而获得基本接近 Native 的性能。这样在基于 JS 的解决方案上就补上了这一环。

从前到后易,从后到前难

谈到跨端和全栈,前面讲的都是出身前端或移动端的编程语言。当然我们也有其他的新的老的语言。那些语言不是说不想做前端,我们发现很多其他语言都尝试过往前端发展,但是都不是很成功。我们可以看到一门编程语言要扩张领域,从前到后比较容易,从后到前就比较难。
这个可能有很多原因了,像之前讲到的非平台钦定语言可能需要额外的 runtime 库就是一个问题。还有比方说架构思想的差异。语言的发展还是首先来自于开发者的需求。“从前到后”的开发者,通常倾向于把前端和后端视作对等,因而采用前后端分离的架构。而对于“从后到前”的开发者来说,出发点可能反而是希望消除前后端分立,把前端作为一个位于整个系统边缘的表现层来解决。从历史上看,这种架构实施起来难度较大,很难平衡用户体验需求和抽象复杂度。虽然这其实并不是语言的锅,但会影响大家对语言适用领域的看法。当然可能还有很多其他原因,我这里不具体展开了,大家可以考虑后面有一些什么样的因素。

新一代前端编程语言趋同

我们前面看到了不同的新的前端和移动端的编程语言,当然它们像它们的前辈一样,仍然会有许多不同,因为在整个发展过程当中,一定要考虑之前的、建立在上一代编程语言上的技术资产。但是总体来说,我们可以发现,这些新一代前端语言是趋同的。

语法

我们发现,新一代前端语言的语法比原来更加接近。很多较新的特性几个语言都有,而且语法是基本一样的。

比如说“?.”运算符,提供了更简洁的 null-safe 的属性访问语法,Kotlin、Swift 和 Dart 都支持。JavaScript 也早就有提案,但是语法上有一点争议,所以一直没有进展,这也导致 TypeScript 团队拒绝实现该特性。不过本月(2019 年 6 月)月初时终于达成一致意见,维持了和其他语言一样的“?.”符号,提案也进入到了 Stage 2。我们也可以期待尽快能在 TypeScript 中使用该特性。

语法的趋同上,最明显的当然是 OC 到 Swift 的转变。Swift 现在跟其他语言是非常像的。包括命名风格,比如前面提到的那个 OC 的“stringByReplacingOccurrencesOfString:withString:”,在 Swift 里就简化了,现在是“replacingOccurrences(of: …, with: …)”,并且借助 extensions 特性, 程序员可以自行加入和其他语言完全一致的“replace()”,这样写出来的代码和其他语言就几乎完全一样了。

类型安全

另外所有新的语言都进一步加强类型安全。

首先就是静态类型支持,像 TS 就给 JS 把静态类型加上了。OC 本来有很多地方也是动态类型的,现在 Swift 则是完全的静态类型。静态类型的引入,对大型编程是非常有帮助的,比方说让 IDE 能具有准确的代码提示和自动完成的能力。

尽管是静态类型,但我们并不需要在每个地方都显式声明类型,因为这些新语言都大量使用自动类型推导,既获得了静态类型的好处,也没有劣化开发者体验。很多时候我们甚至觉得,如果你的代码大部分是胶水代码或应用逻辑,尤其是编写 UI 的时候,好像和用动态类型的语言写出来的代码没有什么区别。

这些新一代语言也都支持泛型。不是所有静态类型的语言都支持泛型,比如 Go(当然 Go 2.0 也要加入泛型)。泛型对于网络服务开发来说或许不一定那么重要,但对于应用开发我认为还是相当重要的。

我们日常开发中经常使用容器类,泛型对于容器类的类型安全来说必不可少。
还有 non-null 和 null-safe,前面提到所有编程语言都在加入这方面的特性,底下可能各有不同,比如 Swift 是基于 Optional type 而没有传统的 null,JS/TS 则有两个 null 值(null 和 undefined)🤪,但面上的语法基本一样,编程体验也很接近,可以说,都比较好的解决了空指针这个“价值亿万美元的错误”。

语言设施

除了语法、类型安全之外,我们还可以看到语言设施也有一个趋同的倾向。举个例子,异步编程设施对于前端开发非常重要,因为不管什么平台,为了保证用户体验,共同的准则都是不能在 UI 线程里做非常 heavy 的操作导致用户操作无响应,这种情况下,异步编程,处理很多异步的操作变成必然要做的事情了,而且面临的挑战其实在各个语言里面也非常类似,比方说 callback hell。所以整个异步编程方面,可以发现新语言也越来越接近。比如很多都是基于 Promise 或者叫 Future 模式。JS/TS 和 Dart 里是作为标准库,Swift 下有 PromiseKit。进一步的,语言可能加入 async/await 语法,JS/TS、Dart 都支持,Swift 也已经有提案,未来也会加入 async/await 特性。Kotlin 的机制虽然可能略有不同,是基于 coroutine 的,但从编程体验上看,也是大同小异。

开发体验

此外,前端语言在开发体验上也互相借鉴。上一个演讲里董博士(Flutter 团队高级研究员:董韬博士)提到了 Hot Reload,这个过去只有 Web 前端开发者可以享受到的便利性。但是现在其他端也都引入了。比如在 Android 上 Instant Run,Flutter 也提供了 Hot Reload,而 SwiftUI 发布时也同时发布了 Xcode Previews。总体来讲,整个体验是往这方面靠拢的。因为这个对于我们开发效率会有提高的。

为什么会趋同

为什么我们在语法、类型安全、语言设施和开发体验等方面会出现趋同?

  • 第一,这几个前端语言的生态位是类似的。我们也有一些很有特色的语言,比如 Elm、Clojure、ReasonML 那样一些语言,都很有特色,但是生态位跟我们前面讲的官方“钦定”的前端语言不一样,它们在源流上本来就基于相对小众的语言(Haskell、Lisp、OCaml),转到前端领域,就算有大厂加持(如 ReasonML 是 facebook 推出的),也只能是所谓 niche,局限在特定人群(各流派函数式编程语言的爱好者,能力很强的小团队等)。我们前面讲的新一代平台“钦定”的前端语言,尽管现在可能只是在一个平台上,但都是希望占据更大的市场的。它们都要抢占“主流工业语言”的生态位,所以他们是以主流工业语言为标的来设计和推广的。在这样相同的生态位上,所有的东西就会有一个相似性。要成为主流工业语言,你要吸引已有的主流工业语言的使用者,尽量靠近传统的 Java、C#,无论是语法、语言特性、编程范式(对 OOP 的支持)之类的,就比较重要。

  • 第二,整个前端开发者的流动,本来原来是 Web 前端开发者,现在做 iOS 或者 Android 的开发,或者反过来。这个趋势非常非常明显。我举一个例子来讲,以前我们觉得 React 只是一个 Web 前端领域的框架,但是 React Native 推出之后,有一次我去听 React Native 的技术分享,会场里有一半以上是 iOS 和 Android 开发者。因为这个流动的趋势,我们希望阻碍流动的因素越少越好。作为新一代“主流工业语言”的竞逐者,互相参考借鉴,就响应了这样一种开发者流动的需求。

  • 第三,本质因为都是前端开发,问题领域非常相似,比如前面已经讲过了异步编程的共性。既然它的问题领域是相似的,最后的结果可能也会殊途同归。因为好东西大家都想用嘛。在这一点上我想特别讲一下 UI DSL。

UI DSL

JS/TS 下的 JSX/TSX,Kotlin 的 Anko,Swift 刚刚发布的 SwiftUI,最后 Flutter,这四个东西,其实在很多地方是非常相似的,即所谓 UI-as-code。当然我们直觉上会认为它们有一些不同,比如我们觉得 JSX 是类 XML/HTML,而后面几个才是真的语言本身的代码。确实表面上是有这个差别。

为什么在 JS/TS 出现 JSX,主要原因是 JS/TS 受到很多既往的限制,不太适合在语言内部做出一个好用好看的 DSL,所以干脆把大家熟悉的 XML/HTML 语法引入作为语法糖,用转译器去转换成等效代码。(当然这个方式其实是有一些后遗症的,比如很难进入语言标准。)相反,Kotlin 在设计之初就把 DSL 作为设计目标,所以可以做出很好的 DSL。Swift 也具有类似的能力。

Flutter 就比较有意思了,Dart 在很多方面很保守,对内部 DSL 设计不是特别友好,所以跟 JS/TS 有相似问题(当然还是比 JS/TS 好多了),它做出的 DSL 不是很好看。不过董韬博士(Flutter 团队高级研究员)已经讲了,已经做了很多改进,包括有一些语言层面的改进,使得 DSL 能看起来更清晰,另外通过 IDE 的改进,也能够弥补在语言层面没有办法完全解决的问题。
无论如何,UI-as-code 这个趋势是共通的。

怎么达到 UI-as-code?

有两个方向:

  • 一个是专用 DSL 代码化,或者说是外部 DSL 内部化。Web 端就是这样,原来的 HTML 变成了 JSX/TSX,原本声明式的外部 DSL 变成可以用代码操纵的对象。包括我们传统 CSS,在这个体系里就倾向于 CSS in JS。方案各种各样,但是都是要把本来单独分离的 CSS 或内嵌于 HTML 的 CSS 转为内嵌到 JS/TS 代码里,由 JS/TS 代码引用和管理。这方式好不好我们另说,但思路就是这样。

  • 另一个,就是 JS/TS 以外的语言,它们是命令式代码的声明化,这个就不多解释了。
    这两个方向,它们在本质上是殊途同归的。最后达到的就是一个声明性 DSL 和命令式程序的结合,或者叫统一。

React

前一段,React 推出了 React Hooks,可以理解为新一代的 UI DSL。像我前面讲趋同,在 React Hooks 上也马上可以看出来,React Hooks 推出之后,我们整个 Android 社区、Swift 社区,包括 Flutter 社区都可以看到马上有开源项目跟进,尝试把 React Hooks 的模式搬运到各个平台上。所以也印证我前面讲的为什么会趋同的那三点,各个平台和语言互相影响和借鉴。React Hooks 作为现在还是一个新的、我认为也比较先进,确实有很多优点的解决方案,我们期待它会不会在不久的将来被吸收到各个平台的官方方案里面去。

Vue

说到 React,我也要提一下 Vue。在不久之前在 VueConf 上面,Vue 作者尤雨溪介绍了 Vue 3 未来的设计。比如 Vue 3 的 function-based component 也借鉴了 React Hooks 的方式,但是跟 React Hooks 还是有一些区别的,比如很“巧合”的避免了 React Hooks 设计上的一些代价。还有一个比较重要的,我们前面讲 UI-as-code,似乎大家都不用模板了,但是这里有一个误解,template 和 UI-as-code 就一定是矛盾的吗?

在 Vue 3 当中做的一个事情就是 template 和 render 做了完美结合。template 是真正声明式的东西,可以做彻底的静态分析。前面讲的现在的那些 UI-as-code 虽然是声明式的,但是本质仍然是 code,很多地方没法静态分析,比如确定到底哪个局部发生了变更,依赖关系是什么,这可能是未来要去解决的问题。

Vue 已经先一步拿出了解决方案。Vue 的设计还是引导开发者尽量使用 template,这样可以充分利用静态信息,同时局部可以自定义 render 实现,获得完全的可控性和灵活性。这样 Vue 可以精确计算出 UI 的变更,使得所需的底层更新操作最小化,获得惊人的性能提升。具体这里不展开了,大家可以去自行了解,这是目前我看到最先进的方案。

所以说单独的 UI-as-code 本身是不是一定是最好的、最终的答案?可能不是。但我们也不用担心,如果是真的好东西,那么按照我前面讲的,一定会流传到各个语言和平台。只不过,我在这里提出来,这也许是一个机会,我们把握到这个趋势,也可以去参与和推动这个趋势,也就是参与创造未来。

未来

预测未来是很危险的事情,90% 的概率是错误的。但是我还是愿意冒险谈一谈。

首先有一点是未来这些前端语言会越来越相似。因为前面讲了看到这样的趋势,我觉得从很多方面,一方面确实看到确实是这样,不同特性如果被证明好的语言特性,或者被证明对开发者是有价值的,那么它会在不同前端语言和平台当中普及开来。

但是另外一方面也要注意,毕竟语言还是有差异的。以下是一些个人的感受。

激进、中庸和保守

我们感觉 Swift 是相对比较激进的语言,被人诟病每年都要学一门新语言。当然 Swift 发展到现在这个版本,基本之前很多东西已经相对稳定下来了,可以认为从狂飙的阶段进入相对稳定的阶段。但是总体上我们还是会感觉,Swift 这个语言和这个社区是不怕做一些激进的改变的。比方说 SwiftUI 也许未来也会有很多的改动。

Kotlin 就是一个相对比较中庸的语言。可能很多人不同意我的看法,觉得 Kotlin 也加入了非常多先进的语言特性,完全不亚于 Swift。确实是这样。但是这个中庸是和 Java 整个平台下其他一些语言比较,Kotlin 非常注重可以复用 Java 现有的东西。所以我觉得它的秉性其实是比较中庸的,中庸在这里不是贬义词,是一个很好的特性。

Dart 这个语言,就是一个相对比较保守的语言,比如引入所有语言新特性的方面有点慢,甚至有时候是迟缓。有一些 Kotlin、Swift 早就有的特性,Dart 就欠奉,一直到确信开发者非常需要这特性,才会提上议事日程。不过保守不一定是坏的,保守有保守的好处。Dart 语言的最初作者本身就是搞 VM 的,要确保某个语言特性在 VM 可以高效运行,对于语言设计来说就有比较高的约束。这也许是 Dart 保守秉性的源头。

TS/JS 未来面临的挑战

最后是 JS/TS,它和前面三个语言有一个重要区别。前面三个语言虽然都是 open source 的,但都是有主导者的。Swift 主导者是 Apple,Dart 主导者是 Google,Kotlin 则是 JetBrains。

TypeScript 虽然是 Microsoft 开发的,但 TS 的未来很大程度上并不取决于 Microsoft,除了类型系统之外,其他都是 JS 带过去的。

JS 这个语言现在是一个比较典型的委员会语言了。这里面其实没有一个非常强势主导方,而是委员会的一群人一起去合作改进的。那么这群人很多方面就会非常不同,我们看 JS 新特性的提案讨论,既有非常激进的动议,也有非常保守的,有一些特性不愿意加。人多嘴杂,事情就难办。而且 TS/JS 其实本质上是从前一代语言(传统 JS)发展而来,它不像其他几个语言是全新的,它还是要背负着不少历史包袱。

JS 社区又非常复杂,差异性很大,我们怎么知道怎么判断社区在总体上的需求和好恶?尤其是当一些设计导致利害冲突时,谁能代表真正社区的心声?所以在这里我就打了一个问号,可能也是出于 JS/TS 需要面临的长期挑战。因为整个语言在发展上,缺乏主心骨,可能面临委员会语言的弊病。

未来的变数

技术上的一个变数是 WebAssembly。前面说过 WASM 可以补齐 JS/TS 的短板。但另一方面,各个其他语言也可以由此进入 JS/WASM 引擎可以运行的所有的端。所以这是一个你中有我我中有你的情况。

会不会有新老语言借此改变前端语言的战局呢?好像也很难说一定没有可能。

这里还想讲的未来当中一个变数,就是我们前面没有提到的小程序。前面提到了 Web 端、Android、iOS 端,但对中国开发者来讲,还有另外一个端是小程序,而且准确讲他还不是一个端,它是有很多很多不同家的“小程序”、“快应用”等,它们虽然非常相似,但是又有一些不同,所以在这个地方其实也是一个非常大的挑战,同时我也觉得这也是一个很大的机遇。

多端的方案,对中国开发者来讲,大家希望把小程序一并解决。前面董博士介绍了 Flutter for Web,可能对于中国开发者来讲还期待有 Flutter for 小程序。如果小程序不能解决,在很多的选型场景下就没有办法选择 Flutter。

选型

所以我们前端的技术栈选型,最大的约束点,第一,平台需求到底跨多少端,这是非常重要的因素。前面讲的就是 Flutter 在这上面有小程序这个缺门。第二,就是团队的技术背景,这个团队本身熟悉一个怎么样的开发工具和开发语言的生态。我们基本还是从这两点进行出发。

中国的公司和产业是否会诞生新语言

最后讲一点想法。我们前面看到的新语言,像 Dart 是 Google 做的,像 Swift 是 Apple 做的,TypeScript 是 Microsoft。这都是大公司。Kotlin 的 JetBrains 不是大公司,但也得到了 Google 的大力支持。Google 和 Microsoft 其实还搞其他语言,Go 啊、C#、F#等。还有像 Facebook,首创了 JSX,搞了 flowtype(很类似于 TS),还搞了 ReasonML、Hack(PHP 版的 TypeScript)等。

其实我觉得中国现在头部的科技公司,从规模来讲,可以认为已经不亚于他们了,但是我们到现在来讲都没有看到任何一个中国的头部公司设计和开发编程语言。当然研究性质的语言可能是有的,比如华为应该是有的,但性质和我们这里讨论的不太一样,不是面向一般程序员的。我们各个公司确实已经做一些自己的平台,比如小程序、快应用等,但是没有搞过编程语言,至少好像没有大张旗鼓的搞,可能内部有偷偷的搞,我们没怎么看到。

为什么一定要做语言

大家可能会有一个疑问,说为什么一定要做语言呢?确实,很多时候我们用即有的语言就可以了,但是我们反过来思考一下,Google、Apple、Microsoft、Facebook 等公司为什么搞新编程语言呢?注意,大家不要想当然觉得人家有强势平台所以可以做这个事情,也不要以为他们总是能搞成功的。实际上有成功的例子,背后一定有更多失败的。

比如 Facebook 的 Skip,就是内部放弃之后才开源出来的,ReasonML 其实也不见得很成功,说不定未来哪天也就放弃了。像 Dart 我前面也提到过,要不是 Flutter,恐怕也没有什么前途。于是有些人会说,连他们都失败了,我们干嘛要做呢?所以好像过去这些年对于搞编程语言这件事情,我们一直是抱持极大怀疑态度的。但是显而易见,你如果永远不去尝试,那么你永远不可能有成功的可能。

当然,我们总可以选择不做这个事情,我们就使用别人做的编程语言,我们搭便车,一样可以享受语言发展的红利。但是这里有一个问题就是,如果我们永远是搭便车的方式,我们就比较会缺乏在编程语言维度解决问题的能力。咱们中国整个头部公司的工程师其实已经都非常厉害了,经过这么多年的锻炼,在我们整个工具箱里面我们有很多的解决的方法,比如库、比如框架,都没有问题,还有很强的 hack 能力,但是编程语言这个维度我们可能是相对比较缺失的一个维度,整个工具箱里缺了一个重要的工具。

从编程语言维度解决问题

什么叫编程语言维度解决问题?举两个例子。

  • 第一个例子就是前面圆心(阿里前端委员会主席)提到了,曾经有一段时间经常做各种 JS Module 的方案。我们可能觉得,用 CommonJS 方案解决就可以了。这个方案不是不好,实际上是一段时间里整个生态的选择。但是 CommonJS 还是有一些问题,它是通过类似库和框架的方式解决的,是一个 runtime 的模块机制,而我们知道今天的 ES6 的模块机制,以及大部分其他语言里的模块机制,是静态的,不是 runtime 的。静态机制有好处,具体这里不展开讲。这里只是用来说明一点,像模块机制,本质上要在语言层面才能得到很好的解决。其实,我们曾经一度很接近这个语言层面的机制,比如玉伯(蚂蚁金服研究员)当年做的 Sea.js 的方案,虽然长得和 CommonJS 一样,实际上 Sea.js 是一个静态模块机制,本质上和 CommonJS 是不同的。Sea.js 已经接触到了语言层级。

  • 另外一个例子就是 async/await,好多年之前有一个中国程序员赵劼,就是著名的百万年薪程序员组合“温赵轮”里的老赵,他其实是一个 C#程序员,他不是 JS 程序员,但是他把 C#/F#中的 async/await 引入了 JS,开发了 Wind.js。

所以呢,我觉得整个中国程序员,就像圆心讲的,我们的思考,我们的能力其实没有问题的,但是似乎有些事情,比如编程语言维度解决问题这一点,没能进入到我们公司的、产业的主线上。老赵做的这个东西非常好,但是一直没有流行起来,我们还是等了 3、4 年,等到外国人做的 JS 编译器加入了这个东西,我们才说,哦,这是个好东西。Sea.js 的情况可能好一点。

当然,这些局部的成果离开发一个完整的新编程语言也还离得比较远,但是我们可以慢慢来,比如 DSL 和 IDE,我们的产业界已经在这方面有实践。前面我们讲到小程序,其实里面就有很多 DSL,每个公司都做了一遍。

这个地方我们已经积累了一些经验,或许从一个小的 DSL 逐渐将来可以走向更加完整或者通用的编程语言。另外 IDE,前面也提到了,现在整个编程语言其实不是单单语言本身,而是包含了整个工具链。微信开发者工具就是个例子。如果我们深入各个公司了解一下,我们发现各个公司各个团队很多都是在做 IDE 上的投资,包括前面圆心提到这事阿里未来的重点方向。

还有,我们不一定现在马上做一个新编程语言,但是我们可以开始贡献现有的编程语言社区,比如董韬博士提到中国程序员对于 Flutter 的贡献,已经非常显著了。

最后是参与编程语言标准制定。比如 ECMA 的 TC39 是 JS 语言的标准委员会,阿里已经在考虑加入,走这样一个流程。我在这边也同步一下我们 360 的情况,上周我们已经向 ECMA 提交了会员申请,并准备加入 TC39 工作组。希望无论是阿里还是 360,或者是其他中国公司,能够代表中国的产业界和开发者去参与这样一个事情。

总结

工欲善其事,必先利其器。对于我们程序员来讲,编程语言就是我们一个最重要的器。所以,我希望今天演讲的内容,包括一些可能比较零碎、松散的想法,能够给大家在整个前端开发和编程语言方面带来更多的理解与思考。谢谢大家!

嘉宾介绍

贺师俊(网名 Hax),360 高级前端架构师,十多年来一直活跃在前端和 JavaScript 社区。对多项 Web 标准有微小贡献,对 Groovy 语言并间接对 Swift 语言有微小贡献,近年来参与了诸多 ECMAScript 新草案的讨论。曾设计和实现 Jedi 语言并用于生产环境,对自研编程语言略有一点实践经验。三次担任 QCon 出品人并获得「优秀出品人」荣誉,也经常在其他众多技术活动中担任讲师、嘉宾和主持人。

评论

发布