前端框架自欺欺人,TypeScript全无必要?

当我们对数据修改时,框架就可以感知这种修改,并对数据所关联的视图进行刷新。

具体是怎么做到的呢?每个框架的实现都不尽相同。

这里以 Vue 的实现简单说一下,当 Vue 按照模板首次渲染时,会收集模板和数据变量的关联关系,相当于视图订阅了数据变量变化的事件,一旦数据发生变化,就会根据这个关联关系,找到对应的视图,并调用它的更新函数。

有了框架帮我们处理好数据和视图的关联,我们就不需要自己显式的管理和操作数据关联的 DOM。我们的心智负担进一步降低,这让我们有更多的精力去开发上层的交互和业务逻辑。

04 为什么当下这么多主流框架?

上面讲了现代框架引入复杂度的好处,那是否可以一个框架就够了呢?这些框架做得都是大同小异的事,为何还需要重复造轮子呢?

有 React,Vue,Angular,近期又有了 Svelte, solid,最近又出现了 Qwik,Astro。

其实每个框架的诞生也有其背景和其想解决的问题。

接下来我们重点聊下现代框架的发展历程,及每个框架的设计哲学和对应的受众。

4.1 React 诞生的意义

首先聊聊 React,2011 年前后, Facebook 的业务快速发展,产生大量需求。

这里需要注意一下当时的背景,这个时期主流的开发方式还是通过 jQuery 直接操作 DOM,手动管理 UI 的状态,自行确保视图和数据之间的状态同步。

随着需求的不断增多,如果继续采用这种传统的开发方式开发 UI 交互,必然会带来后续维护困难的问题。

在这种背景下,要继续叠加需求,只能通过不断加入开发人员,不断产生大量的冗余的交互逻辑和错乱的状态管理。这没有根本解决问题,这个问题需要从设计层面上来优化。

React 就是在这样的背景下诞生,最初只是为了解决 Facebook 内部开发的可维护性低问题。在内部实践取得成功后,再逐步对外推广,在确实解决了其他开发者同样面临的痛点后,最终才成为一个主流的框架。

React 框架的设计理念之一是极简主义

从语法角度上看, React 在传统的前端技术栈上,只引入了 jsx,用于表达虚拟 DOM 的构造过程,其他的一切都是原生的 JavaScript。这样设计的好处是,降低了使用者的学习成本和心智负担,让框架的灵活性极高。比如,我们可以使用原生 Javascript 的 if else 语句表达视图的条件显示,用 for,map 等表达视图中的循环列表,而不需要使用特殊的语法。

从库的职责上看,React 的核心只有 UI,不包含 store,路由等功能,开发者可以自行选择合适的第三方库搭配使用。

React 的另一个设计理念是函数式编程

React 强调把视图的渲染更新当做是一个纯函数,尽量在一部分组件里避免副作用。这样带来的好处是,在代码组织上,组件的状态管理更为内聚清晰,在测试上,组件的可测性更强。

React 的设计理念让 React 使用起来极为的灵活。灵活的好处就是可定制性强,代价缺少约束。所以使用 React 的开发用户要求更高,还有需要配套搭建前端工程化,建立适合自身述求的开发约束。

4.2 为何还有 Vue

既然 React 已经解决了当下的问题,为什么 Vue 还有市场呢?让我们看看 Vue 是怎么走出一条自己的路来的。

早在 2013 年,Vue 是作为尤雨溪的个人实验作品诞生的。发布之后,很快得到一些开发者的认可。比如,PHP 框架 Laravel 的作者 Taylor Otwell 表示,他学习 Vue 的原因是 React 太难学了, Vue 很好入门,使用起来也很简单。这个观点可以代表一部分最早接触 Vue 的人。

正如前面所说,React 当时的设计理念是极简主义,大部分操作都通过 JavaScript 来编写,并且不官方捆绑像状态管理,路由等配套的库。这对于初学者来说并不友好。

早期,web 分工很细,有专门切图的,有专门写 html 的,专门写 css,还有专门写 JavaScript 逻辑的。

所以 React 的设计对于一些不太熟悉 JavaScript 的 Web 开发者来说,并不友好,他们更愿意接受类似 HTML 的写法。

而 Vue 的设计正好符合他们的口味,他们从传统的项目,过渡到 Vue 远远要比过渡到 React 来的简单得多。

这就要讲到 Vue 的设计理念之一,渐进式的开发理念。大白话就是,让框架的初学者更容易接受。

Vue 采用了跟传统 HTML 开发接近的语法,在同一个文件里,通过 template 标签定义模板,script 标签定义 JavaScript 逻辑,在 style 标签内定义样式。初学者从传统 HTML 开发转过来,开发思想的惯性得到了保持,开发 Vue 就像在开发 HTML 一样。

再一个是,Vue 保留了前后端未分离时期,后端模板渲染的那一套,也就是在 HTML 的基础上扩展条件渲染,循环渲染的语法。这让从旧时代后端模板渲染的那些开发者感到格外亲切,也更容易接受。

Vue 的另一个设计理念,开箱即用,俗称 Vue 全家桶。

这是 Vue 的另外一个杀手锏,通过捆绑官方的状态管理 Vuex,路由 Vue-router,让用户免去这些功能的选型困扰,做到开箱即用。这样做的好处是,让一部分没法自行做出合理技术选型的用户,可以在官方的推荐下,被动做出正确的技术选型。

除此之外,Vue 还很贴心的设计了提供了数据响应式的设计,使用者不需要关注数据驱动视图的细节。官网提供非常完善友好的文档,并翻译成多国语言等等。

Vue 的核心设计理念可以总结为:初学者友好向的框架。正是 Vue 的设计者意识到,一部分框架虽然设计思想很先进,但学习成本却比较高,阻挡了一部分用户。所以 Vue 从易用性角度设计框架,不出意外获得大批被其他框架劝退的开发者。

4.3 Svelte

随着 React,Vue 的广泛使用,基于虚拟 DOM 构建前端框架已经成为一种主流的方式。早期对虚拟 DOM 的宣传是,可以减少对 DOM 的操作次数,优化渲染性能。现在这一说法被推翻了,按现在的说法是,虚拟 DOM 是封装了 DOM 的操作细节,降低开发的复杂度。

那虚拟 DOM 是唯一的抽象方式吗?

答案是否定的。Svelte 就是那个推翻虚拟 DOM 垄断的框架。Svelte 创新的提出了基于编译的方式,来解决对 DOM 操作的封装。

为什么 Svelte 要采用编译来解决这一问题呢?

这里需要讲到 Svelte 的设计理念之一:性能优先。正是因为 Svelte 设计的初衷就是做一个轻量级,注重性能的框架,使它抛弃了虚拟 DOM 的方式。虚拟 DOM 需要重复生成虚拟 DOM 树,进行 diff 比对,DOM patch 等操作,这些都是运行时的性能损耗。Svelte 的解决之道是,通过把这些操作提前到编译期来处理,通过编译,生成对应的命令式语句,直接对 DOM 进行更新,有效的把计算从运行时转移到编译期。在 Svelte 的内部,为了追求性能,还通过位运算做变量的变更标记。由于 Svelte 没有传统意义上的运行时,其框架体积也非常小,有利于首屏加载。

Svelte 的另一个设计理念是降低心智负担,具体体现在 Svelte 对数据响应式的设计上。传统的数据响应式,都需要利用到语言的一些 hack 方法来模拟,使用起来其实不太直观,存在一定的心智负担,比如 Vue3 的 ref,需要通过 .value 来取值。而 Svelte 通过编译技术,很好的规避了这个问题。在 Svelte 里,变量定义自然就会获得数据响应的能力,这是因为,在编译时,Svelte 会识别 JavaScript 的赋值语法,并针对这个语法额外生成响应式的代码。这样设计的好处是,开发者可以开发符合他们认知的 JavaScript,并且额外获得数据的响应式,而背后的细节由 Svelte 框架帮忙处理,很好地转移了复杂度。

4.4 各有千秋

前端框架都有自己标榜的核心设计理念,比如 React 不断在复用角度深挖,发明了 hooks 的概念。而 Vue 不断在易用性的角度深挖,发明了 setup 写法,让定义响应式数据,就跟编写普通的 JavaScript 一样简单。而后起之秀 Svelte 和 Solid,则是改变了前端框架在处理 DOM 的常规手段,提出了使用编译的方式来处理数据的响应式,来获得比虚拟 DOM 方式更好的性能。

他们各有优劣,都解决了一部分人的痛点,大家可以结合自己团地和业务的实际情况,选择适合自己的框架。

4.5 整体来看

前端框架之间的关系,如果我们把它们合在一起,作为前端发展历程来看,我们会发现,它们并不是相互排斥的,而是相互借鉴,共同进步的。从整体来看,它们是一个进化的共同体,互相吸收彼此好的东西,摒弃自身不好的东西,最后发展是趋同的。

在这个过程中,跟不上队伍的那个,只能被无情的抛弃,而不断创新的,不断满足开发者诉求,解决痛点的,会继续进化下去。

前端框架除了解决软件设计上的问题外,也跟浏览器的发展密切。

那些炫酷特性的实现,离不开浏览器的发展。比如,Vue 从最开始使用 defineProperty 来实现响应式,到现在使用 proxy ,让响应式的能力更强。

如果一个前端框架仅仅满足于弥补浏览器的不足而存在,那可能在浏览器快速发展的趋势下,会被很快遗忘。比如用来适配浏览器选择器 API 的 jQuery,又或者是某个只有组件封装功能的前端框架,也会被 Web Component 所替代。

只有具备自己的设计理念,并且不满足于这种底层基础的封装的前端框架,才能有机会加入前端框架的竞争之中。

05 知其然知其所以然

所以我们遇到任何事情,都应该知其然知其所以然,了解其背后的原因,了解其实现原理,这样即可以提升我们的认知,也可以帮助我们更好的用好工具,让各种前端框架为我们服务,解决我们实际场景的问题。

比如知道了框架的原理,可以让我们写出更健壮的代码。

很多时候,框架都会给出一些教程,如果我们只是学习了教程,确实是可以开始写代码干活了。但假设我们不知道框架的设计思想,我们不会知道为何要这么写,为何不能那么写。如果遇到教程里没有的,我们就无法变通。

比如以前的老项目,有不少人写 React 时,直接这么设置状态:this.state.xx = 'xxx'。然后发现视图没更新,就用各种奇奇怪怪的方法来触发界面更新。这种写法肯定是错得很离谱的,那为什么会出现这种问题呢,就是因为不愿意多去了解一下框架的运行机制。这么东拼西凑的代码,确实可以碰巧运行起来,但你不知道哪天一个操作就有可能触发严重的 BUG。

只有深入去理解框架的设计思想,我们才能在开发中化繁为简,轻松驾驭各种开发问题的解法。

06 题外话:Typescript 引入复杂度了吗

最近一段时间,还有一个话题很热,就是探讨 TypeScript 是否有必要,是不是引入了过多的复杂度,甚至觉得写类型比写代码还更难。

TypeScript 确实引入了一定的复杂度, 但却是前端往严谨项目开发的必然趋势。

TypeScript 通过给 JavaScript 加上了类型系统,将 JavaScript 中的语言中弱类型带来的陷阱大部分都规避了,大幅提升了系统健壮性和可维护性。

通常我们使用 TypeScript 会有两种场景,一种是开发业务需求,另一种是开发库 / 框架。

那开发业务需求有必要引入 TypeScript 吗?还是要看情况,如果是严谨正规的长周期维护项目,建议是使用,可以避免大量的弱类型语言陷阱,大幅提升系统的可维护性。如果是比较临时,生命周期极短的项目,比如临时开发的简单小需求,不需要持久迭代的,短期内就会下线的,那可以不需要 。

实际上,日常开发业务,我们通常只会使用类型定义,顶多用到泛型函数,类型定义和简单的类型推导,并不会使用到“Typescript 的类型体操”这种模板元编程的程度。如果因为学不会类型体操,而否定 Typescript 在项目里的作用,就有些过了,它们并没有因果关系。

再说说 Typescript 在开发库 / 框架的场景,毋庸置疑,主流的项目基本都采用 Typescript 来开发了。库 / 框架本身就是一种严谨的项目,你开发的东西是要面向广大的开发者的,你有必要保障项目的质量。可能有的人会说,也有的库是用 JavaScript 写的,用其他工具来静态检测不就可以了。确实是可以,不过相比之下,Typescript 的类型系统足够强大,开箱即用,不是很特殊的理由,是不建议去折腾其他的方案。

07 总结

本文因一篇国外的吐槽文而起,里面的观点错得比较普遍、典型,笔者感觉有必要为前端框架做一下澄清,于是写了这篇文章。全文讲述了笔者对前端框架的前世今生的发展历程,特别重点阐述了现代前端框架的诞生的背景和设计理念,并说明其引入复杂度的原因及收益,如有不同观点,欢迎交流探讨。

活动推荐

LLM 时代的大前端还有哪些重点技术需要关注?2024 年,大前端将走向何方?12 月 28-29 日,QCon 全球软件开发大会落地上海,百度、华为、 Intel 、字节跳动等团队专家将探讨 LLM 时代的大前端技术发展趋势与企业级应用开发的机遇和挑战,包括 LLM 如何赋能前端框架调试、如何改善答疑工作,还有 AI 原生应用开发、和 Web 端侧推理的未来、鸿蒙应用开发经验分享,以及最硬核的 IDE 技术。感兴趣的朋友可以扫描下方二维码或点击「阅读原文」,查看大会详细日程。咨询购票可联系票务经理 18514549229。

图片[1]-前端框架自欺欺人,TypeScript全无必要?-JieYingAI捷鹰AI

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享