0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看威廉希尔官方网站 视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

前端框架的Signals有何优势?

OSC开源社区 来源:OSCHINA 社区 2023-10-11 11:04 次阅读

作者|jump--jump

Signals 在目前前端框架的选型中遥遥领先!

国庆节前最后一周在 Code Review 新同学的 React 代码,发现他想通过 memo 和 useCallback 只渲染被修改的子组件部分。事实上该功能在 React 中是难以做到的。因为 React 状态变化后,会重新执行 render 函数。也就是在组件中调用 setState 之后,整个函数将会重新执行一次。

React 本身做不到。但是基于 Signals 的框架却不会这样,它通过自动状态绑定和依赖跟踪使得当前状态变化后仅仅只会重新执行用到该状态代码块。

个人当时没有过多的解释这个问题,只是匆匆解释了一下 React 的渲染机制。在这里做一个 Signals 的梳理。

优势

对比 React,基于 Signals 的框架状态响应粒度非常细。这里以 Solid 为例:

import { createSignal, onCleanup } from "solid-js";

const CountingComponent = () => {
  // 创建一个 signal
  const [count, setCount] = createSignal(0);

  // 创建一个 signal
  const [count2] = createSignal(666);

  // 每一秒递增 1
  const interval = setInterval(() => {
    setCount((c) => c + 1);
  }, 1000);

  // 组件销毁时清除定时器
  onCleanup(() => clearInterval(interval));

  return (
    
count: {count()} {console.log("count is", count())}
count2: {count2()} {console.log("count2 is", count2())}
); };
上面这段代码在 count 单独变化时,只会打印 count,压根不会打印 count2 数据。 控制台打印如下所示:

count is 0

count2 is 666

count is 1

count is 2

...

从打印结果来看,Solid 只会在最开始执行一次渲染函数,后续仅仅只会渲染更改过的 DOM 节点。这在 React 中是不可能做到的,React 是基于视图驱动的,状态改变会重新执行整个渲染函数,并且 React 完全无法识别状态是如何被使用的,开发者甚至可以通过下面的代码来实现 React 的重新渲染。

const [, forceRender] = useReducer((s) => s + 1, 0);

除了更新粒度细之外,使用 Signals 的框架心智模型也更加简单。其中最大的特点是:开发者完全不必在意状态在哪定义,也不在意对应状态在哪渲染。如下所示:

import { createSignal } from "solid-js";

// 把状态从过组件中提取出来
const [count, setCount] = createSignal(0);
const [count2] = createSignal(666);

setInterval(() => {
  setCount((c) => c + 1);
}, 1000);

// 子组件依然可以使用 count 函数
const SubCountingComponent = () => {
  return 
{count()}
; }; const CountingComponent = () => { return (
count: {count()} {console.log("count is", count())}
count2: {count2()} {console.log("count2 is", count2())}
); };

上述代码依然可以正常运行。因为它是基于状态驱动的。开发者在组件内使用 Signal 是本地状态,在组件外定义 Signal 就是全局状态。

Signals 本身不是那么有价值,但结合派生状态以及副作用就不一样了。代码如下所示:

import {
  createSignal,
  onCleanup,
  createMemo,
  createEffect,
  onMount,
} from "solid-js";

const [count, setCount] = createSignal(0);

setInterval(() => {
  setCount((c) => c + 1);
}, 1000);

// 计算缓存
const doubleCount = createMemo(() => count() * 2);

// 基于当前缓存
const quadrupleCount = createMemo(() => doubleCount() * 2);

// 副作用
createEffect(() => {
  // 在 count 变化时重新执行 fetch
  fetch(`/api/${count()}`);
});

const CountingComponent = () => {
  // 挂载组件时执行
  onMount(() => {
    console.log("start");
  });

  // 销毁组件时执行
  onCleanup(() => {
    console.log("end");
  });

  return (
    
Count value is {count()}
doubleCount value is {doubleCount()}
quadrupleCount value is {quadrupleCount()}
); };
从上述代码可以看到,派生状态和副作用都不需要像 React 一样填写依赖项,同时也将副作用与生命周期分开 (代码更好阅读)。

实现机制

细粒度,高性能,同时还没有什么限制。不愧被誉为前端框架的未来。那么它究竟是如何实现的呢? 本质上,Signals 是一个在访问时跟踪依赖、在变更时触发副作用的值容器。 这种基于响应性基础类型的范式在前端领域并不是一个特别新的概念:它可以追溯到十多年前的 Knockout observables 和 Meteor Tracker 等实现。Vue 的选项式 API 也是同样的原则,只不过将基础类型这部分隐藏在了对象属性背后。依靠这种范式,Vue2 基本不需要优化就有非常不错的性能。

依赖收集

React useState 返回当前状态和设置值函数,而 Solid 的 createSignal 返回两个函数。即:

type useState = (initial: any) => [state, setter];

type createSignal = (initial: any) => [getter, setter];
为什么 createSignal 要传递 getter 方法而不是直接传递对应的 state 值呢?这是因为框架为了具备响应能力,Signal 必须要收集谁对它的值感兴趣。仅仅传递状态是无法提供 Signal 任何信息的。而 getter 方法不但返回对应的数值,同时执行时创建一个订阅,以便收集所有依赖信息。

模版编译

要保证 Signals 框架的高性能,就不得不结合模版编译实现该功能,框架开发者通过模版编译实现动静分离,配合依赖收集,就可以做到状态变量变化时点对点的 DOM 更新。所以目前主流的 Signals 框架没有使用虚拟 DOM。而基于虚拟 DOM 的 Vue 目前依靠编译器来实现类似的优化。 下面我们先看看 Solid 的模版编译:

const CountingComponent = () => {
  const [count, setCount] = createSignal(0);
  const interval = setInterval(() => {
    setCount((c) => c + 1);
  }, 1000);

  onCleanup(() => clearInterval(interval));
  return 
Count value is {count()}
; };

对应编译后的的组件代码。

const _tmpl$ = /*#__PURE__*/ _$template(`
Count value is `); const CountingComponent = () => { const [count, setCount] = createSignal(0); const interval = setInterval(() => { setCount((c) => c + 1); }, 1000); onCleanup(() => clearInterval(interval)); return (() => { const _el$ = _tmpl$(), _el$2 = _el$.firstChild; _$insert(_el$, count, null); return _el$; })(); };

执行 _tmpl$ 函数,获取对应组件的静态模版

提取组件中的 count 函数,通过 _$insert 将状态函数和对应模版位置进行绑定

调用 setCount 函数更新时,比对一下对应的 count,然后修改对应的 _el$ 对应数据

其他

大家可以看一看使用 Signals 的主流框架:

Vue Ref

Angular Signals

Preact Signals

Solid Signals

Qwik Signals

Svelte 5 (即将推出)

不过目前来看 React 团队可能不会使用 Signals。

Signals 性能很好,但不是编写 UI 代码的好方式

计划通过编译器来提升性能

可能会添加类似 Signals 的原语

PREACT 作者编写了@preact/signals-react 为 React 提供了 Signals。不过个人不建议在生产环境使用。

编辑:黄飞

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • API
    API
    +关注

    关注

    2

    文章

    1499

    浏览量

    61989
  • 函数
    +关注

    关注

    3

    文章

    4329

    浏览量

    62588
  • 代码
    +关注

    关注

    30

    文章

    4786

    浏览量

    68557
  • Signals
    +关注

    关注

    0

    文章

    2

    浏览量

    1018

原文标题:聊聊前端框架的未来Signals

文章出处:【微信号:OSC开源社区,微信公众号:OSC开源社区】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    赛灵思基于FPGA平台的PFM电机控制方案优势

    PFM为什么是一种比PWM更好的电机控制算法?赛灵思基于PFM算法的电机控制方案到底优势?详见本文分析...
    发表于 07-23 10:18 2249次阅读

    ARM-based相比ARM cortex优势

    你看好ARM-based架构吗 相比ARM cortex优势 ARM其他还有什么架构啊,感觉曝光的好少。。
    发表于 04-24 06:55

    在电机控制上28069比28335优势

    在我的认识中28069与28335在硬件上的主要差别在于28069比28335多了vcu和cla两个模块,请问:1.在电机控制上28069比28335优势?2.在controlSUITE/development_kits/H
    发表于 11-29 20:25

    主流web前端威廉希尔官方网站 框架

    BootstrapBootstrap是一款很受欢迎的前端框架,基于HTML、CSS、JavaScript设计的,简单灵活,使得Web开发更加快捷,Bootstrap中包含了丰富的Web组件和13个jquery插件
    发表于 03-28 16:56

    labview操作者框架有什么优势

    labview操作者框架采用的面向对象编程,到底什么优势呢?目前不知道这个框架优势
    发表于 05-21 14:45

    请问如何看待svelte这个前端框架

    如何看待 svelte 这个前端框架?看了下。十分精简。思路也很独特。
    发表于 06-01 05:55

    微内核什么时候发布?微内核优势

    想了解下微内核什么时候发布的?微内核优势
    发表于 10-10 10:09

    学好前端必须要弄懂的框架

    间过度框架,对“新手”极其友好,学习了jQuery框架的思想,再开始学习Vue 和 React就简单了。为了让更多同学能轻松学会前端框架,信盈达小编给大家分享一个jQuery
    发表于 06-30 16:57

    学好前端必须要弄懂的框架

    间过度框架,对“新手”极其友好,学习了jQuery框架的思想,再开始学习Vue 和 React就简单了。为了让更多同学能轻松学会前端框架,信盈达小编给大家分享一个jQuery
    发表于 07-06 11:18

    网表仿真与RTL仿真相比优势

    网表仿真与RTL仿真相比优势?“线与”逻辑是什么?
    发表于 11-04 06:23

    目前流行的前端开发框架是什么

    00. 目录文章目录00. 目录01. 概述02. 跨多平台开发框架03. 移动端混合开发框架04. 前端开发框架05. 附录01. 概述作为前端
    发表于 11-08 06:52

    Android框架里的相关默认权限操作作用

    Android框架里的相关默认权限操作作用?
    发表于 02-17 06:23

    前端开发】一篇文章概括目前流行的前端开发框架

    00. 目录文章目录00. 目录01. 概述02. 跨多平台开发框架03. 移动端混合开发框架04. 前端开发框架05. 附录01. 概述作为前端
    发表于 11-03 11:36 25次下载
    【<b class='flag-5'>前端</b>开发】一篇文章概括目前流行的<b class='flag-5'>前端</b>开发<b class='flag-5'>框架</b>

    腾讯开源的前端框架介绍

      今天推荐两个腾讯开源的前端框架,分别是 wujie(无界)和 Omi。 wujie(无界) 无界微前端是一款基于 Web Components + iframe 微前端
    的头像 发表于 06-17 14:44 2137次阅读
    腾讯开源的<b class='flag-5'>前端</b><b class='flag-5'>框架</b>介绍

    四线制测电阻优势?与二线制测电阻区别?

    前言四线测试法被认为是目前为止最好的消除引线电阻引入误差(或将其降至最小的)的测试方案。那么,四线测试法优势?它的原理是什么?与二线制测电阻
    的头像 发表于 05-12 09:43 2.1w次阅读
    四线制测电阻<b class='flag-5'>有</b><b class='flag-5'>何</b><b class='flag-5'>优势</b>?与二线制测电阻<b class='flag-5'>有</b><b class='flag-5'>何</b>区别?