谈谈我对 Reacitive 方法的理解

本文我想和大家分享一下我对当前 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
js 复制代码
function Counter() {
  const [count, setCount] = useState(0)
  return <button onClick={() => setCount(count + 1)}>{count}</button>
}
  • Angular
js 复制代码
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
js 复制代码
<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
js 复制代码
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
js 复制代码
<script>
  import { writable } from 'svelte/store';

  const count = writable(0);

  function increment() {
    // Update the count by incrementing it
    count.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
js 复制代码
export const Counter = component$(() => {
  const count = useSignal(123);
  return (
    <button onClick$={() => count.value++}>
      {count.value}
    </button>
  );
});
  • SolidJS
js 复制代码
export const Counter = (() => {
  const [count, setCount] = createSignal(123);
  return (
    <button onClick={() => setCount(count() + 1)}>
      {count()}
    </button>
  );
});
  • Vue
js 复制代码
<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 开启了一种很酷的编码方式,它允许你可视化系统的响应式并调试它。

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

相关推荐
.生产的驴3 分钟前
Electron Vue框架环境搭建 Vue3环境搭建
java·前端·vue.js·spring boot·后端·electron·ecmascript
awonw5 分钟前
[前端][easyui]easyui select 默认值
前端·javascript·easyui
九圣残炎26 分钟前
【Vue】vue-admin-template项目搭建
前端·vue.js·arcgis
柏箱1 小时前
使用JavaScript写一个网页端的四则运算器
前端·javascript·css
TU^1 小时前
C语言习题~day16
c语言·前端·算法
学习使我快乐014 小时前
JS进阶 3——深入面向对象、原型
开发语言·前端·javascript
bobostudio19954 小时前
TypeScript 设计模式之【策略模式】
前端·javascript·设计模式·typescript·策略模式
黄尚圈圈5 小时前
Vue 中引入 ECharts 的详细步骤与示例
前端·vue.js·echarts
浮华似水6 小时前
简洁之道 - React Hook Form
前端