Web 框架能解决什么问题?

2022 年 4 月 7 日 InfoQ

作者 | Noam Rosenthal
译者 | Sambodhi
策划 | 闫园园
Noam Rosenthal 对各个框架的共性技术特性进行了深入的探讨,并说明了各种框架是怎样实现这些特性的,并说明了其成本。

最近,我对对比框架和普通的 JavaScript 产生了浓厚的兴趣。这始于我在一些自由职业项目中使用 React 时遇到的一些挫折,以及我最近作为规范编辑,对 Web 标准有了更多的认识。

我希望了解一下这些框架的共性和差异,Web 平台作为一种更精简的选择,能提供什么,以及它是否足够。我的目标并非要抨击这些框架,而是要了解成本和效益,找出有没有其他选择,甚至当我们决定采用框架时,我们也能从中吸取教训。

在本系列文章的第一部分中,我将深入探讨一些跨框架的共性技术特性,并介绍几种不同的框架是怎样实现这些特性的。我还要看一下使用这些框架的成本。

框    架

我选取四种架构进行研究。React 是当今的主流框架,还有三个较新的竞争者,它们声称自己的工作方式与 React 不同。

  • React

“React 使创建交互式用户界面变得不费力。声明性视图使你的代码更可预测,更容易调试。”

  • SolidJS

“Solid 遵循与 React 相同的理念……但它的实现方式完全不同,放弃了使用虚拟 DOM。”

  • Svelte

“Svelte 是一种全新的构建用户界面的方式……是一个在你构建应用时发生的编译步骤。Svelte 不使用虚拟 DOM diffing 之类的技术,而是编写代码,当你的应用程序的状态发生变化时,外科手术式地更新 DOM。”

  • Lit

“在 Web Components 标准的基础上,Lit 增加了……反应性、声明性模板,以及一些深思熟虑的特性。”

总结一下这些框架对其差异化的说法。

  • React 通过声明式视图使构建 UI 更容易。

  • SolidJS 遵循 React 的理念,但是采用了另一种技术。

  • Svelte 处理用户界面采用了一种编译时的方式。

  • Lit 使用现有的标准,并增加了一些轻量级的特性。

框架能解决什么问题?

框架自身也提及了诸如声明性、反应性和虚拟 DOM 等词。让我们深入了解它们的含义。

声明性编程

声明性编程是一种范式,在这种范式中,逻辑被定义,而没有指定控制流。我们描述需要的结果是什么,而不是我们会采取什么步骤。

在 2010 年左右,声明性框架的早期,DOM 的 API 更加简单,更加冗长。而使用命令式的 JavaScript 编写 Web 应用程序则需要大量的模板代码。这时,“模型 - 视图 - 视图模型”(model-view-viewmodel,MVVM)的概念开始盛行,当时具有划时代意义的 Knockout 和 AngularJS 框架,提供了一个 JavaScript 声明层,在库内处理这种复杂性。

今天,MVVM 并不是一个广泛使用的术语,它在某种程度上是旧术语“数据绑定”的变种。

数据绑定

数据绑定是一种声明性的方式,用来表示数据如何在模型和用户界面之间同步。所有流行的 UI 框架都提供了某种形式的数据绑定,它们的教程都以数据绑定的例子开始。

以下是 JSX(SolidJS 和 React)中的数据绑定:

function HelloWorld() { const name = "Solid or React";
return ( <div>Hello {name}!</div> )}

Lit 中的数据绑定:

class HelloWorld extends LitElement { @property() name = 'lit';
render() { return html`<p>Hello ${this.name}!</p>`; }}

Svelte 中的数据绑定:

<script>  let name = 'world';</script>
<h1>Hello {name}!</h1>
反应性

反应性是一种声明性的方式来表达更改的传播。

如果我们能够用一种声明的方式来表示数据绑定,那么我们就必须要有一个使框架能够传播更改的高效方法。

React 引擎会把渲染的结果与之前的结果相比较,并将差异应用于 DOM 本身。这种处理更改传播的方式,被称为虚拟 DOM。

在 SolidJS 中,这是以其存储和内置元素更明确地完成的。例如,Show 元素将跟踪内部的变化,而不是虚拟 DOM。

在 Svelte 中,生成“active”代码。Svelte 知道哪些事件会导致变化,它会生成直接的代码,区分事件和 DOM 更改。

在 Lit 中,反应性是通过元素属性来实现的,基本上是依赖 HTML 自定义元素的内置反应性。

逻辑

如果框架为数据绑定提供了声明性的接口,并且能够实现反应性,那么就必须提供一些方法来表达一些传统意义上的逻辑,这些逻辑是以命令的方式写的。逻辑的基本构件是 “if” 和 “for”,而所有的主流框架都提供了这些构件的一些表达。

 条件句

除了绑定数字和字符串等基本数据外,每个框架都提供了一个“条件”原语。在 React 中,它看起来如下所示:

const [hasError, setHasError] = useState(false);  return hasError ? <label>Message</label> : null;setHasError(true);

SolidJS 提供了内置的条件组件。

<Show when={state.error}>  <label>Message</label></Show>

Svelte 提供了 #if 指令:

{#if state.error}  <label>Message</label>{/if}

在 Lit 中,你将在 render 函数中使用显式三元运算:

render() { return this.error ? html`<label>Message</label>`: null;}
 列表

另一个常见的框架基元是列表处理。列表是用户界面的一个关键部分——如联系人列表、通知等——要想高效工作,就必须有反应性,而不是在一个数据项发生变化时,对整个列表进行更新。

在 React 中,列表处理看起来像这样:

contacts.map((contact, index) => <li key={index}>   {contact.name} </li>)

React 使用特殊的 key 属性来区分列表项,它确保整个列表不会在每次渲染时被替换。

在 SolidJS 中,使用了 for 和 index 内置元素。

<For each={state.contacts}>  {contact => <DIV>{contact.name}</DIV> }</For>

在内部,SolidJS 将自身的存储与 for 和 index 相结合,以确定在项目发生个更改时要更新哪些元素。它比 React 更清晰,使我们能够避免虚拟 DOM 的复杂性。

Svelte 使用 each 指令,该指令根据其更新器被转译:

{#each contacts as contact}  <div>{contact.name}</div>{/each}

Lit 提供了一个 repeat 函数,它的工作原理类似于 React 的基于键的列表映射:

repeat(contacts, contact => contact.id,    (contact, index) => html`<div>${contact.name}</div>`
组件模型

有一件事超出了本文的范围,那就是不同框架中的组件模型,以及如何使用自定义 HTML 元素来处理它。

注意:这是一个很大的主题,我想在以后的文章里讨论这个主题,因为这个主题会让这篇文章变得太长。

成    本

框架提供了声明性的数据绑定、控制流原语(条件和列表),以及传播更改的反应性机制。它们还提供了其他重要的东西,比如重用组件的方法,但这就是另一篇文章的主题了。

框架有用吗?是的。它们带给了我们所有这些方便的特性。但这是一个正确的问题吗?使用框架需要付出一定的成本。让我们来看一下这些成本。

包大小

在查看包大小时,我更愿意看到非 Gzip 的缩减大小。这个尺寸与 JavaScript 的 CPU 开销有很大关系。

  • ReactDOM 大约是 120 KB。

  • SolidJS 大约是 18KB。

  • Lit 大约是 16KB。

  • Svelte 约为 2KB,但生成的代码大小不同。

现在看来,在保持包大小上,现在的框架要优于 React。虚拟 DOM 要求使用很多 JavaScript。

构建

不知何故,我们习惯了“构建” Web 应用。如果不设置 Node.js 和 Webpack 这样的捆绑器,不处理 Babel-TypeScript 启动包中最近的一些配置更改,以及所有这些事情,就不可能启动一个前端项目。

越是有表达力的框架,包大小就会变得更小,但构建工具和转译时间的负担就越大。

Svelte 宣称,虚拟 DOM 完全是一种开销。我同意,但是可能像 Svelte 和 SolidJS 这样的“构建”以及像 Lit 这样的自定义客户端模板引擎都只是单纯的开销吗?

调试

在构建和转译过程中,需要付出的成本也是不同的。

我们在使用和调试 Web 应用程序时,所见到的代码和我们所编写的完全不一样。我们现在依靠同样品质的调试工具,逆向设计出一个站点,并把它和我们自己的代码中的 bug 相关联。

在 React 中,调用栈从来不是“你的”事情——React 会为你处理调度。这一特性在没有 bug 的时候非常好用。但是,如果你试图找出无限循环重现的原因,你将会陷入痛苦的境地。

在 Svelte 中,库本身的包大小很小,但你要传输和调试一大堆神秘的生成代码,这些代码是 Svelte 对反应性的实现,根据你的应用需求定制。

Lit 并不需要进行大量的构建,但是要想有效地进行调试,你就必须熟悉其模板引擎。这也许是我对框架持怀疑态度的最大原因。

当你寻求自定义的声明式解决方案时,你将面对更加困难的命令调试。本文中的示例采用了 TypeScript 来对 API 进行规范,但是该代码本身并不需要转译。

升级

在本文中,我讨论了四个框架,但是还有许多其他的框架,多得数不清(AngularJS、Ember.js 和 Vue.js,仅举几例)。你能指望框架、它的开发者、它的思想和它的生态系统在开发过程中为你工作?

除了修补自己的 bug 之外,还有一个更让人沮丧的事情,就是必须为框架的错误找到变通方法。而且,还有一个更加令人沮丧的事情,那就是在没有修改你的代码的情况下,将框架升级为新的版本,会出现 bug。

诚然,浏览器中也有这样的问题,但是这种问题一旦出现,就会影响到所有人,而且在大多数情况下,修复或者发布一个解决方案,都是迫在眉睫的。此外,本文提到的大部分模式都建立在成熟的 Web 平台 API 之上,并不一定都需要采用尖端技术。

总    结

我们对框架所要处理的核心问题有了更深刻的理解,并且着重于数据绑定、反应性、条件和列表。我们也对成本进行了讨论。

在本系列的第二部分中,我们将会了解到,在没有框架的情况下,我们是怎样处理这些问题的,以及我们可以从中学习到什么。敬请关注!

作者介绍:

Noam Rosenthal,Web 平台顾问,WebKit 和 Chromium 的贡献者,标准编辑,也是经验丰富的 Web 开发者。他的工作主要是在 Web 开发和浏览器 / 标准开发之间架起桥梁。

原文链接:

https://www.smashingmagazine.com/2022/01/web-frameworks-guide-part1/

今日好文推荐

泛型会让你的 Go 代码运行变慢

HTML5崛起之时,Java桌面时代就已经终结了

互联网企业给被裁员工发“毕业须知”;孟晚舟担任华为轮值董事长;腾讯员工被曝偷看创业公司工作文档 | Q资讯

云端开发是个坑!4年后,我们又回到了本地环境                               


 活动推荐

长期征集|寻找中国卓越技术团队

2022 年第一季《中国卓越技术团队访谈录》即将上线,本期精选了包括腾讯云鼎实验室、优麒麟、PingCAP、西门子 Mendix、火山引擎 ByteHouse、搜狗输入法无障碍产品在内的优秀团队,敬请关注。同时,访谈录现开放长期报名通道,如果你身处传统企业经历了数字化转型变革,或者正在互联网公司进行创新技术的研发,并希望 InfoQ 可以关注和采访你所在的技术团队,就请抓住机会吧!

点个在看少个 bug 👇

登录查看更多
0

相关内容

React.js(React)是 Facebook 推出的一个用来构建用户界面的 JavaScript 库。

Facebook开源了React,这是该公司用于构建反应式图形界面的JavaScript库,已经应用于构建Instagram网站及 Facebook部分网站。最近出现了AngularJS、MeteorJS 和Polymer中实现的Model-Driven Views等框架,React也顺应了这种趋势。React基于在数据模型之上声明式指定用户界面的理念,用户界面会自动与底层数据保持同步。与前面提及 的框架不同,出于灵活性考虑,React使用JavaScript来构建用户界面,没有选择HTML。Not Rest

专知会员服务
12+阅读 · 2021年9月8日
【干货书】Python科学编程,451页pdf
专知会员服务
126+阅读 · 2021年6月27日
【干货书】PyTorch实战-一个解决问题的方法
专知会员服务
143+阅读 · 2021年4月2日
【2020新书】Python专业实践,250页pdf,Practices of the Python Pro
Web 框架的替代方案
InfoQ
0+阅读 · 2022年4月14日
解决事件驱动型微服务中的并发问题
InfoQ
0+阅读 · 2022年4月10日
Web 3.0 很火,但它到底怎么来的?
AI前线
1+阅读 · 2022年3月30日
2022 年十大 JavaScript 框架
AI前线
0+阅读 · 2022年1月28日
「Web3之父」回答:Web3究竟是什么?
新智元
4+阅读 · 2022年1月10日
跨平台开发框架 Compose Multiplatform 1.0 发布
容器并不能解决一切问题
InfoQ
0+阅读 · 2021年11月18日
微信小程序支持webP的WebAssembly方案
前端之巅
19+阅读 · 2019年8月14日
国家自然科学基金
0+阅读 · 2013年12月31日
国家自然科学基金
1+阅读 · 2013年12月31日
国家自然科学基金
1+阅读 · 2013年12月31日
国家自然科学基金
0+阅读 · 2012年12月31日
国家自然科学基金
0+阅读 · 2012年12月31日
国家自然科学基金
0+阅读 · 2012年12月31日
国家自然科学基金
3+阅读 · 2011年12月31日
国家自然科学基金
1+阅读 · 2009年12月31日
国家自然科学基金
1+阅读 · 2009年12月31日
国家自然科学基金
0+阅读 · 2009年12月31日
Arxiv
0+阅读 · 2022年4月20日
Arxiv
0+阅读 · 2022年4月20日
Arxiv
0+阅读 · 2022年4月19日
Arxiv
0+阅读 · 2022年4月15日
VIP会员
相关资讯
Web 框架的替代方案
InfoQ
0+阅读 · 2022年4月14日
解决事件驱动型微服务中的并发问题
InfoQ
0+阅读 · 2022年4月10日
Web 3.0 很火,但它到底怎么来的?
AI前线
1+阅读 · 2022年3月30日
2022 年十大 JavaScript 框架
AI前线
0+阅读 · 2022年1月28日
「Web3之父」回答:Web3究竟是什么?
新智元
4+阅读 · 2022年1月10日
跨平台开发框架 Compose Multiplatform 1.0 发布
容器并不能解决一切问题
InfoQ
0+阅读 · 2021年11月18日
微信小程序支持webP的WebAssembly方案
前端之巅
19+阅读 · 2019年8月14日
相关基金
国家自然科学基金
0+阅读 · 2013年12月31日
国家自然科学基金
1+阅读 · 2013年12月31日
国家自然科学基金
1+阅读 · 2013年12月31日
国家自然科学基金
0+阅读 · 2012年12月31日
国家自然科学基金
0+阅读 · 2012年12月31日
国家自然科学基金
0+阅读 · 2012年12月31日
国家自然科学基金
3+阅读 · 2011年12月31日
国家自然科学基金
1+阅读 · 2009年12月31日
国家自然科学基金
1+阅读 · 2009年12月31日
国家自然科学基金
0+阅读 · 2009年12月31日
Top
微信扫码咨询专知VIP会员