谈谈我对 Reacitive 方法的理解

news/2025/3/5 6:29:09/

本文我想和大家分享一下我对当前 Reactivity 方法和现状的理解。我并不是说我的观点就是对的,但我认为,正是通过分享自己的观点,我们才能对行业中的事物达成共识,我希望这些来之不易的见解能够对其他人有所帮助,并补充他们理解中缺失的部分。

reacitve 三剑客

我认为到目前为止,我们在行业中看到的 reacitive 方法有三种:

  1. 基于 value:也就是脏检查,应用的框架有 Angular, React, Svelte;
  2. 基于 observable : 应用的框架有 Angular with RxJS, Svelte;
  3. 基于 singnal:应用的框架有 Angular with signals, Qwik, React with MobX, Solid, Vue

接下来我来谈谈这三种方法:

基于 value

基于 value 的系统依赖于将状态作为简单值存储在“不可观察”引用中。

当我 说“observable” 时,我并不是指的是像 RxJS 这样的可观察对象。我指的是“可观察”这个词的常用用法,比如知道它什么时候发生了变化。“不可观察”意味着当值发生变化时,没有办法及时知道具体的实例。下面我给出三个例子:

  • React
function Counter() {const [count, setCount] = useState(0)return <button onClick={() => setCount(count + 1)}>{count}</button>
}
  • Angular
import { Component } from '@angular/core';@Component({selector: 'app-counter',template: `<h1>Counter: {{ count }}</h1><button (click)="increment()">Increment</button>`,
})
export class CounterComponent {count: number = 0;increment() {this.count++;}
}
  • Svelte
<script>let count = 0;function increment() {count += 1;}
</script><div><h1>Counter: {count}</h1><button on:click={increment}>Increment</button>
</div>

在上面的每种情况下,状态都作为一个值存储在变量中。但关键是它是一个不可观察的值,以一种不允许框架知道(观察)值何时变化的方式存储在 JavaScript 中。

由于该值的存储方式不允许框架观察到变化,因此每个框架都需要一种方法来检测这些值何时发生变化,并将组件标记为脏组件。

一旦标记为 dirty,就会重新运行组件,以便框架可以重新读取/重新创建值,从而检测哪些部分发生了更改,并将更改反映到 DOM。

脏检查是基于 value 的系统所能采用的唯一策略。它将最后一个已知值与当前值进行比较

那怎么知道什么时候运行脏检查算法呢?通常不同的框架方式不同:

  • Angular: 隐式依赖 zone.js 来检测状态何时可能发生了变化。(因为它依赖于通过zone.js 的隐式检测,所以运行变更检测的频率比严格必要的要高。)
  • React: 显式依赖于开发人员调用 setState()
  • Svelte: 自动生成 setState() 调用

基于 Observable

Observable 对象是随时间变化的值。Observable 对象允许框架在值发生变化时及时知道具体的实例,因为将新值推送到 Observable 对象中需要特定的 API 来充当保护。

可观察对象是解决细颗粒 Reacitive 问题的明显方法。但是,因为 observable 需要显式调用 .subscribe() 和相应的调用 .unsubscribe(),导致开发体验不好 。可观察对象也不能保证同步无故障的交付,UI 倾向于同步更新。下面我们给出代码示例:

  • Angular
import { Component } from '@angular/core';
import { Observable, BehaviorSubject } from 'rxjs';@Component({selector: 'app-counter',template: `<h1>Counter: {{ count$ | async }}</h1><button (click)="increment()">Increment</button>`,
})
export class CounterComponent {private countSubject = new BehaviorSubject<number>(0);count$: Observable<number> = this.countSubject.asObservable();increment() {this.countSubject.next(this.countSubject.value + 1);}
}
  • Svelte
<script>import { writable } from 'svelte/store';const count = writable(0);function increment() {// Update the count by incrementing itcount.update(n => n + 1);}
</script><div><h1>Counter: {$count}</h1><button on:click={increment}>Increment</button>
</div>

Svelte: 有趣的是,它有两个具有不同心智模型和语法的 Reacitive 系统。这是因为基于value 的模型只在 .svelte 文件中工作,所以将代码移出 .svelte 文件需要一些其他的 Reacitive 原语(Stores)。

我相信每个框架都应该有一个可以处理所有用例的单一 Reacitive 模型,而不是基于用例的不同 Reacitive 系统的组合。

基于 Signal

Signal 就像可观察对象的同步表兄弟没有订阅/取消订阅。我相信这是一个重大的编码改进,我也相信 Signal 是未来。

Signal 的实现并不明显,这就是为什么行业花了这么长时间才走到这一步。Signal 需要与底层框架紧密耦合,以获得最佳的编码体验和性能。

为了获得最好的结果,需要协调框架渲染和可观察对象更新。下面给出几个示例:

  • Qwik
export const Counter = component$(() => {const count = useSignal(123);return (<button onClick$={() => count.value++}>{count.value}</button>);
});
  • SolidJS
export const Counter = (() => {const [count, setCount] = createSignal(123);return (<button onClick={() => setCount(count() + 1)}>{count()}</button>);
});
  • Vue
<template><section><h1>Count: {{ count }}</h1><button @click="incrementCount">+1</button></section>
</template><script setup>
import { ref } from 'vue';const count = ref(0);
function incrementCount() {count.value++;
}
</script>

Angular 正在研究 Signal ,但它们仍然需要 Signal 和模板的集成。

最后,总结一下我的观点。

可观察对象太复杂了,不太适合。因为只有 BehaviorSubject 可观察对象才能真正与 UI 一起工作。

在基于 Value 的系统中,性能又是极其消耗的。虽然值的变化不会破坏应用程序,只是当有一天你觉它太慢了的时候,并且当你想要进行优化它时,就会发现没有“明显”的东西需要修复。

对于基于 Signal 的系统,对于开发者,最初的理解门槛会稍微高一些,并且开发者很有可能从 Reacitive 悬崖上掉下来。因为如果你对 Signal 的反应错误,应用程序就会崩溃。但是解决问题的办法也会很明显。

其次,当一旦你开始优化基于 Value 的系统的时候,你就会开始接触到基于 Signal 的世界,在那里你可能会像处理 Signal 一样失去 Reacitive。本质上,基于 Value 的“优化”API是“低于标准的 Signal 的”。

这也是我喜欢 Signal 的第二个原因。Signal 开启了一种很酷的编码方式,它允许你可视化系统的响应式并调试它。

好啦,以上就是我的理解,希望对你有帮助!


http://www.ppmy.cn/news/1184754.html

相关文章

10391 - Compound Words (UVA)

题目链接如下&#xff1a; Online Judge 代码如下&#xff1a; #include <iostream> #include <string> #include <vector> #include <set> // #define debugint main(){#ifdef debugfreopen("0.txt", "r", stdin);freopen(&qu…

VPS是什么?详解亚马逊云科技Amazon Lightsail(VPS)虚拟专用服务器

2006年&#xff0c;南非开普敦&#xff0c;亚马逊推出了WBS&#xff0c;以网络服务的形式向企业提供基础的IT服务。亚马逊云科技的一小步&#xff0c;在无数技术更迭&#xff0c;天才设计师和程序员的努力与基础设施建设的完善之下成为了人类科技进展的一大步。 亚马逊云科技可…

leetCode 260.只出现一次的数字 ||| + 位运算

260. 只出现一次的数字 III - 力扣&#xff08;LeetCode&#xff09; 给你一个整数数组 nums&#xff0c;其中恰好有两个元素只出现一次&#xff0c;其余所有元素均出现两次。 找出只出现一次的那两个元素。你可以按 任意顺序 返回答案。你必须设计并实现线性时间复杂度的算法且…

k8s day10

JENKINS集成K8S项目实战 部署Jenkins环境: 1.下载Jenkins软件包 curl -o jenkins-k8s.zip http://192.168.17.253/Kubernetes/day10-/softwares/jenkins-k8s.zip 2.解压软件包 yum -y install unzip unzip jenkins-k8s.zip 3.安装JDK环境&#xff0c;如上图所示 cd jenki…

【JavaSE】运算符详解及与C语言中的区别

在文章的最后&#xff0c;总结了Java与C语言的某些不同点 目录 一、什么是运算符 二、算术运算符 1.基本四则运算符 2.增量运算符 3.自增/自减运算符/-- 三、关系运算符 四、逻辑运算符&#xff08;重点&#xff09; 1.逻辑与&& 2.逻辑或|| 3.逻辑非 4.补…

RK3588开发笔记-USB3.0接口调试

目录 前言 一、资源介绍 二、硬件连接 三、设备树配置

假如我有一台服务器,我会让它提供三种服务

一、提供照片上传、存储和下载服务 随着移动互联网时代的持续快速发展&#xff0c;PC互联网日益势微&#xff0c;各大互联网门户网站的博客、空间也跟着凋零&#xff0c; 作为博客、空间的标配功能的相册也随之被关闭。 2019年3月6日网易相册发布停运公告并于当年5月8日正式停…

LeetCode 541 反转字符串 II 简单

题目 - 点击直达 1. 541 反转字符串 II 简单1. 题目详情1. 原题链接2. 题目要求3. 基础框架 2. 解题思路1. 思路分析2. 时间复杂度3. 代码实现 1. 541 反转字符串 II 简单 1. 题目详情 给定一个字符串 s 和一个整数 k&#xff0c;从字符串开头算起&#xff0c;每计数至 2k 个…