AICon议程上新60%,阿里国际、360智脑、科大讯飞、蔚来汽车分享大模型探索与实践 了解详情
写点什么

React 不是真正的响应式编程,Svelte 才是

  • 2019-05-25
  • 本文字数:4558 字

    阅读完需:约 15 分钟

React不是真正的响应式编程,Svelte才是

这个题目可能有点夸张,但不管怎样 Svelte 和它的理念就是这样的。如果你还没听说过 Svelte 的话就去了解一下吧——你会见证一场革命的,它将取得空前的成就(没有给 Svelte 团队增加压力的意思)。


本文不是 Svelte 的入门教程。Svelte 团队已经做了一份很棒的交互式手把手入门教程,能帮助你轻松迈入响应式编程的天地。


Svelte 教程地址:https://svelte.dev/tutorial/basics


先来份免责声明吧:我不是编程高手,我也没有无所不知。我只是非常热衷于每天出现的创新事物,喜欢随时随地谈论它们而已——这也是这篇文章的来历。请客观对待我说的每一句话,如果我哪里跑了火车也请告诉我吧。


那好,我们这就开始正题吧!

首先,谈谈 React

讨论为什么我认为 Svelte 有如此革命性的突破之前,咱们先来看看之前 Dan 发布的这条推文,研究一下它背后的含义:



另一个免责声明:本文可没有批判 React 的意思。我只是拿 React 来举个例子说明问题,因为大多数读者迟早都会用到它的。提 React 只是因为它是跟 Svelte 对比的最佳例子罢了。


Dan 上面那句话究竟是什么意思?这对我们现在写代码的方式有什么影响?要回答这个问题,我先简单介绍一下 React 的工作机制吧。


当你呈现一个 React 应用时,React 会在所谓虚拟 DOM 中保留 DOM 的副本。虚拟 DOM 充当你的 React 代码与浏览器绘制到 DOM 的内容之间的中间层。


然后当你的数据出现变动时(可能因为你调用了 this.setState,useState),React 会做一些工作来确定如何在屏幕上重新绘制 UI。


它会对比虚拟 DOM 与真实的 DOM,以确定数据更新导致了哪些更改。然后它会仅重新绘制与虚拟 DOM 中的新副本不匹配的 DOM 部分,这样就无需在每次数据更新时重新绘制整个 DOM 了。


这就显著提升了性能,因为更新虚拟 DOM 比更新真实 DOM 要节省很多资源,而 React 只更新真实 DOM 中需要改变的部分。有一篇文章很好地解释了这一过程:


https://medium.com/@gethylgeorge/how-virtual-dom-and-diffing-works-in-react-6fc805f9f84e


但你可能会发现这个实现有点问题。如果你没有告诉 React 你的数据已经改变了(比如说调用 this.setState 或 Hooks 之类),那么虚拟 DOM 就不会有变化,React 也不会随之响应(Duang!搞砸了!)。


这就是 Dan 所说的,React 并不是完全的响应式设计的意思。React 需要你手动跟踪应用数据,并在数据变化时告诉 React,这也意味着你得做更多工作。

好了,现在该谈 Svelte 了

Svelte 是一种构建 UI 的全新途径,它速度极快、效率极高,是真正的响应式设计,还不需要虚拟 DOM;用 Svelte 写的代码比其它任何框架或库都更加简洁。


说得这么好听,可你肯定会问它和其它一大堆 JavaScript 库和框架究竟有什么区别呢?我来逐一说明吧,

真正的响应式设计

Svelte 既不是库也不是框架;相反,Svelte 是一个编译器,它吃进你的代码并吐出与你的 DOM 直接交互的原生 JavaScript,不需要中间层。


等等,什么?编译器?是的——编译器。这个思路太强悍了,我都不知道为什么以前没人想得到呢?为什么这个主意这么棒,听我细细道来吧。


引一句 Rich Harris 在 YGLF 2019 大会上的讲话:


Svelte 3.0 将响应设计从组件 API 移到了编程语言中。


这说的是啥?别急,我们已经看到 React 和大多数其他前端框架,要求你在更新其虚拟 DOM 之前,使用 API 来告诉它数据已更改(再次通过调用 this.setState 或 useState)。


在 React 以及大多数 UI 框架和库中,调用 this.setState 意味着你的应用的响应能力是与特定的 API 绑定的,没有 API 它就没法知道数据什么时候变动了。


Svelte 采取了另一种方法解决这个问题。


它从运行代码的方式中获取了 Observable 的灵感。 它不是从上到下运行代码,而是以拓扑顺序运行它。 查看下面的代码片段,我们将了解以拓扑顺序运行它的含义。


1. (() => {2.   const square => number => number * number;3.4.   const secondNumber = square(firstNumber);5.   const firstNumber = 42;6.7.   console.log(secondNumber);8. })();
复制代码


现在如果你按从上到下的顺序运行这几行代码的话就会在第 4 行遇到错误,因为 secondNumber 依赖 firstNumber,而这时候 firstNumber 尚未初始化。


如果以拓扑顺序运行这段代码则不会出现任何错误。为啥呢?编译器并不会按从上到下的顺序运行这段代码;相反,它会查看所有变量并生成依赖图(比如说 A 依赖 B 才能工作之类)。


这算是对编译器如何以拓扑顺序编译代码的简化解释了。


1. 这个新变量'square'是否依赖其它变量?      - 它没有,所以我会初始化它2. 这个新变量'secondNumber'是否依赖其它变量?      - 它依赖'square'和'firstNumber'。我已初始化'square',但我还没有初始化'firstNumber',马上就会做。3. 好的,我已初始化'firstNumber'。现在我可以使用'square'和'firstNumber'初始化'secondNumber'了      - 我是否拥有运行此console.log语句所需的所有变量?      - 是的,所以我会运行它了。
复制代码


乍看上去代码好像是从上到下的运行顺序,但仔细观察就会发现它的确是跳着执行的。


跑到第 4 行时,编译器发现它没有 firstNumber,因此会暂停执行并查看代码,找出它是不是在别的地方定义了。一看,原来它是在第 5 行定义的,所以编译器会先运行第 5 行,然后返回第 4 行继续执行。


如果语句 A 依赖于语句 B,则语句 B 会先运行,运行顺序与声明的顺序无关。


那么这和 Svelte 实现真正的响应式设计又有什么关系?具体来说,你可以在 JavaScript 中用标识符标记一个语句,如下所示:的标识符(如果之前未定义 foo,则严格模式下会出错)。


所以在这种情况下,当 Svelte 看到任何带有 $:前缀的语句时,它就知道左边的变量要从右边的变量中获取值。我们现在有了一种方法可以将一个变量的值绑定到另一个变量。


响应!这意味着我们现在正在使用 JavaScript 的 API 核心部分来实现真正的响应设计,无需摆弄像 this.setState 这样的第三方 API。


实践中是这个样子:


1. // vanilla js2. let foo = 10;3. let bar = foo + 10; // bar is now 204. foo = bar // bar is still 20 (no reactivity)5. bar = foo + 10 // now bar becomes 30
6. // svelte js7. let foo = 10;8. $: bar = foo + 10; // bar is now 209. foo = 15 // bar is now 25 because it is bound to the value of foo
复制代码


请注意,在上面的代码中我们不需要将 bar 重新分配给 foo 的新值——比如直接通过 bar = foo + 10;或者通过调用像 this.setState({ bar = foo + 10 });这样的 API 方法,现在都用不着了。它会自动为我们处理好的。


这意味着当你将 foo 更改为等于 15 时,bar 会自动更新为 25,并且你不必调用 API 来为你更新它。Svelte 已经知道了。


上面的 Svelte 代码的编译版本如下所示:


1. ... omitted for brevity ...2. function instance($$self, $$props, $$invalidate) {3.   let foo = 10; // bar is now 204.   $$invalidate('foo', foo = 15) // bar is now 25 because it is bound to the value of foo5.   let bar;6.   $$self.$$.update = ($$dirty = { foo: 1 }) => {7.     if ($$dirty.foo) { $$invalidate('bar', bar = foo + 19); }8.   };9.   return { bar };10. }11. ... omitted for brevity ...
复制代码


好好花点时间研究一下上面这段代码吧,慢慢来,不要着急。


看到在 bar 被定义之前 foo 是如何更新的了吗? 那是因为编译器正在以拓扑顺序,而非严格的自上而下的顺序在解析 Svelte 代码。


Svelte 会自己响应数据变化。它用不着你操心更改的内容和时间;它自己就会知道。


注意:在第 4 行里,bar 的值到下一个 Event Loop 之前都不会更新的,这样一切都会干净又整洁。


这样你就不必在数据发生变化时手动更新状态了。你可以专注于你的代码逻辑,而 Svelte 可以帮助你将 UI 与最新状态协调好。

简洁

前面我不是说 Svelte 可以用更少的代码来完成更多工作吗?事实确实如此。下面我拿 React 中一个简单的组件和 Svelte 中的对应组件举个例子,你自己看:



17 行对 29 行代码,这俩应用的功能完全相同,看看我们在 React.js 中编写了很多的代码吧——这我还没开始用 Angular 呢。



Svelte 代码除了更简洁耐看外也更容易理解,因为它的活动部件比 React 代码少。我们不需要事件处理程序来更新输入元素的值——只需绑定值即可。


回想你刚刚开始学习网页开发的时候。哪边的代码会让你更难理解?左边的还是右边的?


虽然这看起来没那么重要,但当你开始构建更大、更复杂的应用时,很快就会发现不用写那么多代码是多么有用。我曾花了好几个小时试图理解同事编写的大型 React 组件是如何工作的。


我确实相信 Svelte 的简化 API 能使我们更快地阅读和理解代码,从而提高整体工作效率。

性能

好了,现在我们已经知道 Svelte 是真正的响应式设计,可以让你用更少的投入做更多的事情。那么它的性能如何?完全用 Svelte 编写的应用能有很好的用户体验吗?


React 之所以如此强大,其原因之一在于它使用虚拟 DOM 来更新应用程序的 UI,一次只更新一部分,无需在每次更改内容时重新构建整个 DOM(这非常消耗资源)。


但这种方法的缺点是,如果组件的数据发生变化,React 将重新渲染该组件及其所有子组件,哪怕子组件不需要重新渲染也得这么干。这就是为什么 React 会有 shouldComponentUpdate、useMemo、React.PureComponent 一类的 API。


只要使用虚拟 DOM 在状态更改时渲染 UI,这个问题就没法解决。


Svelte 不使用虚拟 DOM,那么它如何解决重新绘制 DOM 以匹配应用程序状态的问题呢?这里我再次引用 Rich Harris 在 YGLF 上的精彩演讲:


框架不是用于组织代码的工具。它们是组织你思想的工具。


Rich 认为框架可以在构建步骤中运行,从而让代码在运行时无需中间层。这也就是为什么 Svelte 是编译器而非框架的原因。


这就是为什么 Svelte 速度飞快的原因。Svelte 将你的代码编译为一个直接与 DOM 交互的高效底层代码。但 Svelte 是如何解决数据更改时重新绘制整个 DOM 的问题呢?


像 React 这样的框架需要你调用 API 方法,在数据发生变化时告诉它;但使用 Svelte 时,只需使用赋值运算符=就足够了。


如果状态变量——比如说 foo ——使用=运算符更新,则 Svelte 将仅更新依赖 foo 的其它变量,如前所示。这让 Svelte 可以仅绘制 DOM 的一部分内容,这些部分以某种方式从 foo 中获取它们的值。


我将省略实际的实现方式,因为这篇文章已经足够长了。你可以看看 Rich Harris 自己的解释:


https://www.youtube.com/watch?v=AdNJ3fydeao

结语

Svelte 3.0 是最近软件开发业的福音之一。有些人可能会说这是夸大其词,但我不这么认为。Svelte 背后的理念及其实现将使我们能向浏览器发送更少的 JS 模版,却做更多的事情。


反过来,这会带来性能更强、更轻量的应用程序,并生成更易阅读的代码。那么现在,Svelte 将很快取代 React、Angular 或其它流行前端框架吗?


现在我可以说答案是否定的。与它们相比 Svelte 相对年轻,所以它需要时间来成长、成熟,并解决一些我们可能还没发现的问题。


就像 React 诞生后改变了软件开发产业一样,Svelte 也有可能改变我们对框架的看法,以及我们开发新事物时的思路。


英文原文:https://blog.logrocket.com/truly-reactive-programming-with-svelte-3-0-321b49b75969



2019-05-25 14:5617963

评论 8 条评论

发布
用户头像
如果真的遇到大量dom更新的场景,Svelte性能还能比React16好?
2020-05-09 14:57
回复
用户头像
vue比 react 更加响应式的啊
2019-06-03 14:29
回复
用户头像
学前端真的很容易望山跑死马呀。。。
2019-06-03 09:18
回复
用户头像
代码比较直观,减少了学习框架的成本
2019-05-28 20:44
回复
用户头像
这编辑器有点无语啊。。。我的回车换行都没了(哭
2019-05-28 16:02
回复
用户头像
无意冒犯,但react的核心思想从来都不是所谓的响应式编程吧?

然后对Svelte说一下自己的看法

首先,这种理念是挺独特的。相当于用编译器直接代替框架了。但目前来看,Svelte的做法还相对来说太灵活,不是说灵活不好,但对于很多前端er来说,灵活≈工程化程度低。

我几乎能想象到,在一个中型前端项目中,满屏幕的全局变量(相对于script标签)、完全搞不清的作用域边界、一大堆散落的 独立的方法等等,这对于中大型项目可以说是灾难性的。

我觉得他的理念很好,但想要在工程中使用,还是需要一定程度的封装来达到一个比较好的可用性的。
展开
2019-05-28 16:01
回复
用户头像
不用 v-dom,自己实现响应式。换句话说,咋不用angular?
2019-05-26 22:57
回复
用户头像
感觉理念比vue/react先进
这谁学的动啊
2019-05-26 12:52
回复
没有更多了
发现更多内容

tidb之旅——资源管控

TiDB 社区干货传送门

新版本/特性解读 7.x 实践

云数据库是杀猪盘么,去掉中间商赚差价,aws数据库性能提升 10 倍!价格便宜十倍。

TiDB 社区干货传送门

数据库架构设计 7.x 实践

IT运维的福音!WeOps综合服务让运维更简单

嘉为蓝鲸

运维 IT weops

万字好文:大报文问题实战 | 京东物流技术团队

京东科技开发者

MySQL 网关 报文 企业号 7 月 PK 榜 大报文

新能力提升全面预算管理效率和效力

用友BIP

全面预算

aws上采用tidb和原生使用aws rds价格的比较。兼数据分析性能的测试

TiDB 社区干货传送门

TiDB 底层架构 性能测评 7.x 实践

# 文盘Rust -- FFI 浅尝

TiDB 社区干货传送门

开发语言

数据库运维实操优质文章分享(含Oracle、MySQL等) | 2023年6月刊

墨天轮

MySQL 数据库 oracle postgresql 国产数据库

活动预告|7月29日 Streaming Lakehouse Meetup·北京站

Apache Flink

大数据 flink 实时计算 信息推送

华为开发者大会:软件开发小白的华为云云上初体验

华为云PaaS服务小智

云计算 软件开发 华为云 华为开发者大会2023

TiDB v7.1.0 资源管控功能是如何降低运维难度和成本-实现集群资源最大化?

TiDB 社区干货传送门

实践案例 版本测评 性能测评 应用适配 7.x 实践

京东统一头尾管理系统探索实践 | 京东云技术团队

京东科技开发者

管理系统 企业号 7 月 PK 榜 头尾管理

gRPC 接口调试利器,让你成为高效开发者

Apifox

程序员 gRPC RPC 开发 RPC 协议实现原理

TiKV集群断电(灾难)恢复过程记录

TiDB 社区干货传送门

6.x 实践

TiDB v7.1.0 跨业务系统多租户解决方案

TiDB 社区干货传送门

实践案例 新版本/特性解读 应用适配 HTAP 场景实践 7.x 实践

加速布局,用友为国产化替代保驾护航!

用友BIP

国产替代

数智化赋能企业,开启全新商业模式

用友BIP

国产替代

tidb之旅——tidb架构选择

TiDB 社区干货传送门

迁移 安装 & 部署 6.x 实践

tidb之旅——dm工具篇

TiDB 社区干货传送门

迁移 安装 & 部署 6.x 实践

一份保姆级的Stable Diffusion部署教程,开启你的炼丹之路 | 京东云技术团队

京东科技开发者

人工智能 AI绘画 Stable Diffusion 企业号 7 月 PK 榜

亿级日活业务稳如磐石 华为云发布性能测试服务CodeArts PerfTest

华为云PaaS服务小智

云计算 软件开发 性能测试 华为云

这10个强大的CSS属性,每个前端都要懂

伤感汤姆布利柏

简单三步完成离线升级TIDB v7.1(服务器无互联网环境)

TiDB 社区干货传送门

版本升级 7.x 实践

TiDB 7.1.0 LTS 特性解读 | 资源管控 (Resource Control) 应该知道的 6 件事

TiDB 社区干货传送门

版本测评 新版本/特性解读 7.x 实践

阿里云瑶池数据库出席2023可信数据库发展大会,PolarDB荣获多项评测证书

科技热闻

tidb之旅——生成列

TiDB 社区干货传送门

新版本/特性解读 7.x 实践

《2022-2023年中国大数据市场研究年度报告》正式发布,腾讯云位列领导者行列

Geek_2d6073

科研类项目核算的“法、术、器”(一)

用友BIP

项目云

快速提效,便捷易用 | 嘉为蓝鲸数字化运营中心全方位体验升级

嘉为蓝鲸

运维 IT weops

索引加速功能真能提升10倍吗?--TiDB V6.1.0-V7.1.0建索引速度对比

TiDB 社区干货传送门

版本测评 性能测评 7.x 实践

TiDB 7.1 资源管控验证测试

TiDB 社区干货传送门

版本测评 新版本/特性解读 7.x 实践

React不是真正的响应式编程,Svelte才是_语言 & 开发_Ovie Okeh_InfoQ精选文章