1.3 全栈开发语言JavaScript和TypeScript
在JetBrains 2022年度开发者生态报告中,JavaScript被评为最广泛使用的编程语言,而TypeScript是增长最快的编程语言。报告中另一个值得关注的数据是整个开发社区75%与Web应用相关。JavaScript的广泛应用使其拥有繁荣的生态系统和强大的生命力。截至2023年4月底,npmjs.com上已经有319万个npm包,每周下载量达到519亿次,每月下载量达到2094亿次。
1.3.1 JavaScript的发展历程
根据MDN官方文档,JavaScript是一个涵盖性术语,在浏览器环境下,由两个部分组成,一个是核心语言,即ECMAScript;另一个是一系列Web接口的合集,包括DOM(文档对象模型)等。
1995年,Brendan Eich在Netscape公司发明了JavaScript语言。JavaScript是Sun公司的注册商标,后来被甲骨文公司收购。1997年,由于商标问题,Netscape公司将JavaScript语言提交给ECMA标准组织,成了ECMA标准,ECMA-262是这个标准的官方名称,并在同年发布了ECMAScript 1。负责ECMA-262的委员会名为TC39。现代所有的JavaScript引擎(如V8)都实现了ECMAScript标准。
随着Web的发展,越来越多的浏览器开始支持JavaScript,但是实际上在那个时候,JavaScript还没有被定位为像今天这样严肃的编程语言。由于开发时间短,早期JavaScript存在着许多性能问题和安全问题。然而,如果开发者想在浏览器中进行逻辑操作,只能选择JavaScript,因此,JavaScript仍然得到了广泛的使用和发展。此外,硬件的不断发展也很好地弥补了JavaScript造成的性能问题。尽管性能一直不是这门语言设计的首要目标,但是2008年谷歌推出的Chrome的V8引擎极大地改善了这一问题。
2009年5月,Ryan Dahl发布了Node.js第一个版本,提供了通过JavaScript构建服务器应用的能力,初始版本只支持Linux和macOS平台。同一年,JavaScript发布了ECMAScript 5,提供了严格模式Array/Object操作方法等功能。
2010年1月,一个为Node.js环境提供包管理的工具npm诞生了。
2012年11月,TypeScript发布了第一个公开预览版本0.8.1。
2013年6月,Nicholas C.Zakas发布了ESLint的初始版本。
2013年12月,尤雨溪发布了Vue的初始版本。
2015年6月,ECMAScript的第6个版本发布,也被称为ES6。ES6是一次非常大的版本变更,类似于Python 2和Python 3之间的关系。由于ES5和ES6之间的差异非常大,社区创造了一个专门用于转换这两者的工具Babel。
每年,TC39会组织多次会议,讨论JavaScript的新特性,并推出ECMAScript的新版本。如果一个新特性有TC39的支持者,它就会进入Stage 0。如果在TC39组织的会议中经过评议并成熟,它就会进入Stage 1。为了进入这个阶段,该提议一定会有一个官方的拥护者来负责这个提案相关的工作。通常到了Stage 1,提议就会有完整的文档,包括使用用例、高层级的API、潜在问题以及去实现的挑战。JavaScript社区会在GitHub或TC39的论坛上对这些问题进行讨论。进一步成熟后,通过TC39的下一次评议,就会进入Stage 2。一个议题到了Stage 2通常会有非常详尽的关于新功能的文档,所有相关问题已经讨论得差不多,并且后续的更改也不应该是大范围的更改了,在进一步收集反馈和讨论后,一个功能就可能进入Stage 3。进入Stage 3,基本表示这个功能已经是最终要实现的功能了,这时浏览器厂商、JavaScript上下游工具就会开始实现对这些新功能的支持,比如TypeScript对JavaScript标准装饰器的支持就是在JavaScript的装饰器到了Stage 3之后引入的。最后就是Stage 4,相关测试已经完善,TC39的成员对于这一功能已经很熟悉,相关标准的文档也已经完善。每年ECMAScript的新版本包括的新特性都是到了Stage 4的特性。
1.3.2 TypeScript:从21%到69%
2012年,微软内部的一个团队找到Anders Hejlsberg,询问是否可以把C#的代码编译为JavaScript。这样,C#的作者就可以把一些C#的库移植到JavaScript环境中。当时微软内部已经意识到JavaScript是真正的跨平台语言,其方便性使得内部已经有很多很复杂的JavaScript应用。当时,JavaScript的开发环境还不像现在这样成熟,而C#有完善的开发工具链,所以开发这样的工具是非常合理的需求。随着微软内部对于JavaScript大型应用的编写尝试,他们深刻认识到,使用JavaScript维护大型应用是一项非常困难的任务。在2012年,由于缺乏类型定义和相应的工具链,即便是像微软这样有着众多技术精英的公司,也几乎不可能重构一个规模较大的JavaScript代码。Anders意识到了JavaScript的价值,同时也看到了它存在的问题。因此,他开始思考如何解决这个问题,这也是TypeScript产生的初衷。
正如前面提到的,TypeScript是成长速度最快的语言。从图1-8中不难发现,TypeScript几乎是2017—2022年间唯一有这样增长速度的编程语言。
•图1-8 JetBrains 2022年开发者生态报告中编程语言使用趋势
Wakatime是一个记录IDE使用时间的插件,已有40万注册用户在各种编程语言上的实际投入时间被记录下来。根据2022年发布的统计报告显示,TypeScript首次超越JavaScript,成为Wakatime用户投入时间最多的编程语言,详见图1-9。
•图1-9 Wakatime 2022年度用户使用时长统计
在TypeScript诞生之时,很多人对它持怀疑态度,认为将静态类型引入JavaScript这样的“玩具”语言似乎是一个玩笑。然而,随着VS Code的推出,开发者逐渐习惯了代码补全、导航和重构等功能,并逐渐习惯了有类型的开发方式。
TypeScript应该是少有的,从建立开始就不是以颠覆前辈编程语言为目的,而是甘愿作为辅助,与JavaScript共同发展的编程语言。从某种角度来说,TypeScript只是JavaScript的静态代码检查器的DSL,因为TypeScript的核心设计理念是:
• 不施加运行时开销。
• 与当前和未来的ECMAScript建议保持一致。
• 保留所有JavaScript代码的运行时行为。
• 避免添加表达式级语法。
• 使用一致的、完全可擦除的结构类型系统。
使用TypeScript相较于单纯编写JavaScript会多出很多代码,而这些多出来的代码几乎都不会在最终运行时中存在,而是为开发者提供服务。深度使用TypeScript的用户会定期与TypeScript官方进行互动。每当TypeScript发布新版本时,Google/Bloomberg都会发来反馈。谷歌有专门的团队负责TypeScript的使用,并开发了便利的工具,能对所有代码同时进行新版本的编译检测。由于TypeScript几乎不影响运行时,它是少有的升级成本相对较低的编程语言。
如果TypeScript想要增加运行时的特性,他们会积极与TC39进行沟通,在JavaScript的标准中增加。TypeScript并不期望替代任何工具,而是期望与JavaScript发展浪潮中产生的任何组件/工具很好地集成。TypeScript官方和JavaScript生态工具作者的合作是非常紧密的,无论是线上线下的活动,还是GitHub的issue区,都可以看到这种紧密的联系。esbuild作者Evan Wallace在TypeScript的GitHub的issue区提出的问题质量都非常高。
本质上,TypeScript的使用和JavaScript的使用是交织在一起的,每个TypeScript开发者也是JavaScript开发者。在JavaScript年度调研报告首次发布时,TypeScript仅有21%的使用率,但到了2021年,TypeScript的使用率已经增长到了69%。TypeScript的成功超出了所有人的预期,这门语言的成功要归功于TypeScript的贡献者、社区组织者、花时间解答和教授新人的专家、学习和在社区提问题的新用户。他们对TypeScript倾注了心血,证明了这门语言有着广阔的前景。
1.3.3 从框架到框架无关
Web世界的变化非常迅速。起初,网页服务只是把静态文档托管在服务器上,任何人都可以通过互联网访问。随着人们对页面的需求不断增加,逐渐出现了一些专门为Web而生的技术,如PHP。PHP可以在HTML中直接访问后端代码,从而实现动态内容,并将变化的内容存储到后端数据库中。随着动态内容需求的不断增加,如Flash等技术也应运而生。随后,JavaScript的应用开始显现,著名的jQuery得到了广泛的应用。页面被做得越来越复杂,开发者开始探索MVC、MVVM等架构,同时也在不断探索更为复杂的应用场景。
随着单页面应用(Single-page application, SPA)慢慢成为主流,复杂且庞大的单页面应用需要许多小组件之间实现数据同步,复杂性导致Bug层出不穷。为解决这个问题,Facebook的软件工程师Jordan Walke在2011年编写了FaxJS,后来改名为React。React的出现解决了页面状态和页面渲染之间的同步问题,将页面渲染放在DOM上,而将页面状态处理放在JavaScript中。
2013年,还在谷歌工作的尤雨溪做了一个非常庞大的UI界面,因为这个界面非常复杂,且有很多重复的HTML,他开始寻找一些工具来帮助完成这项工作。然而,他发现没有现成的工具、库或者框架能满足他的需求。因此他决定开发一个工具来帮助自己,这就是Vue.js的前身。
随着React、Vue等工具的诞生和广泛使用,开发者可以编写越来越复杂的前端应用,这些工具采用声明式编程风格的内核也催生了开发者们对组件式开发的推崇。与此同时,前端开发界对工具的需求也变得越来越强烈,Webpack、Rollup、ESLint、Prettier等优秀的前端工具也应运而生。每个框架都形成了自己的生态系统,开发一个应用程序需要选择一个框架,再从该框架的路由、状态、hooks库及UI库中挑选。这导致了一个问题,即框架绑定和隔离。在一个框架中创建的库通常不能在另一个框架中使用。
近年来,这个问题得到了一些改善,TanStack在2022年开始做框架无关化改造,其Query项目前身是React Query,目前经过改造,已经可以同时支持React、Vue、Solid和Svelte。实际上,这些年前端框架之间相互学习借鉴,响应式核心的实现都有相似的地方,这为实现类似TanStack项目提供了可能性。在前端开发中,不熟悉CSS的开发者会选择使用对应框架的UI库,这样可以提高开发效率,但是在深入使用后可能需要修改样式。通常UI库为了提供简便的用户接口,会对CSS/SASS进行深度封装。因此,如果要修改UI库的样式,就需要理解其封装的代码逻辑,这样的成本较高。Tailwind的诞生解决了这个问题。站在使用UI库的角度看,Tailwind提供了标准的样式基础协议,解耦了CSS和JavaScript之间的关系,Tailwind社区发展非常健康,出现了如DaisyUI这样的框架。DaisyUI是封装在Tailwind之上的框架,只提供了CSS相关的封装,这样如果想要修改其中的逻辑,也只需要处理样式相关的代码即可,降低了二次开发的成本。还有很多优秀的项目都在尝试框架无关化,如Volar。Volar最初诞生于为Vue提供VS Code的语言服务,现在正在孵化Volar.js,期望对所有框架提供语言服务。
随着框架的不断涌现,出现了许多优秀的项目。这些项目不断发展壮大,逐渐与原始框架解耦,形成了框架无关的新项目。或许在不久的将来,前端开发将不再像2022年那样,需要学习如此多相似的技术,而是只需要掌握像TypeScript这样的几个项目即可。开发者不再疲于奔命地学习新技术,而是可以更聚焦在自己的业务上。