Angular 进阶之五: Signals到底用不用?

news2025/2/11 8:51:07

Angular 在V16的时候推出了Signals,在17正式作为主打功能之一强烈推荐,看过了各种博主的各种科普文章也没说明白,到底这东西值不值得用?毕竟项目大了,重构代码也不是闹着玩儿的。各种科普文章主要在说两点:

1. 用了性能提高

2. 用了方便,你改一个,使用的地方自动就获得通知。(听起来和Rxjs的Observer一样)

在Angular项目日常开发过程中主要的矛盾其实就两个。

1. Object的成员改了,不会触发子组件(component)的ngOnChange。需要手动调用detectChanges。

2. 性能,其中之一就是html template里头不能用function,不然会不停的调用。

这也是Signals可能可以解决的两个问题。我们用下面一个简单的例子来说明。

import { Component, signal, computed } from '@angular/core';

interface Member {
    id: number;
    name: string;
    value: number;
}

@Component({
    template: `<div>
        <p>{{targetsSignal()[0].name}} has {{targetsSignal()[0].value}}</p>
        <button (click)="increase()">Increase</button>
        
        <button (click)="decrease()" [disabled]="canDecrease()">Decrease</button>
        <p>Name starts with M</p>
        <div *ngFor="let member of computedNames()">{{member.id}} - {{member.name}}</div>
        
        <div style="height: 300px; width: 300px; background-color: #2f487e;" (mousemove)="onMousemove()"></div>
        <test-signal-sub [members]="targetsSignal()"></test-signal-sub>
    </div>`,
    selector: 'test-signal',
})

export class SignalComponent {
    counter = 0;
    counter1 = 0;

    targets: Member[] = [{name: 'Maria', id: 1, value: 1}, {name: 'Michel', id: 2, value: 1}, {name: 'Jack', id: 3, value: 1}, {name: 'John', id: 4, value: 1}, 
                {name: 'Sam', id: 5, value: 1}, {name: 'Mila', id: 6, value: 1}];
    readonly targetsSignal = signal<Member[]>(this.targets);
    
    computedNames = computed(() => {
        this.counter++
        console.log("namesFiltered() called! " + this.counter + " times!");
        return this.targetsSignal().filter(item => item.name.startsWith('M'));
    })

    constructor() {

    }

    onMousemove() {
        // console.log('Mouse moved');
    }

    increase() {
        const newTargets = [].concat(this.targetsSignal());
        newTargets[0].value += 1;
        this.targetsSignal.set(newTargets);
    }

    decrease() {
        this.targetsSignal.update(val => { 
            val[0].value -= 1;
            return val;
        });
    }

    canDecrease() {
        this.counter1++;
        console.log("canDecrease() called! " + this.counter1 + " times!");
        return this.targetsSignal()[0].value <= 0;
    }
}
import { Component, OnChanges, SimpleChanges, Input } from '@angular/core';

@Component({
    template: `<div>
        <p>Sub signal component below:</p>
        <p>{{name}} preferred {{color}}</p>
        <p>{{members[0].name}} has value {{members[0].value}}</p>
    </div>`,
    selector: 'test-signal-sub',
})

export class SignalSubComponent implements OnChanges {
    @Input() members: any;
    color = '';
    name = '';
    readonly preferedColor = [{name: 'Maria', color: 'red'}, {name: 'Michel', color: 'blue'}, {name: 'Jack', color: 'yellow'}, {name: 'John', color: 'green'}, 
    {name: 'Sam', color: 'black'}, {name: 'Mila', color: 'white'}];
    constructor() {

    }

    ngOnChanges(changes: SimpleChanges): void {
        if (changes.members?.previousValue !== changes.members?.currentValue) {
            const index = changes.members?.currentValue[0].value % changes.members?.currentValue.length ?? 0;
            this.color = this.preferedColor[index].color;
            this.name = this.preferedColor[index].name;
        }
    }
}

两个组件,父组件的html template里绑定了computedNames,和canDecrease,从外表也看不出来区别,但是如果使用鼠标在蓝色区域晃动,控制台就会打印canDecrease被不停调用,computedNames没事,这个例子说明了Signal可以被用在html template里,解决的主要场景之一就是button的状态,现实中不都是理想情况,button属于form,一个valid状态就解决了button是不是disable。很多情况button的[disabled]后面跟了一串判断,"a || b || !c || d"等等,现在有了signal就直接一个signal就好了。但是重构时需要把涉及到的a,b,c,d这些成员都改成signal。这就可能需要一次重构一整个component。

在上面例子中increase使用set每次用新数组更新signal,decrease使用update,每次只改原来的数组,在子组件中,会看到,increase会触发子组件的ngOnChange,但是decrease不会。这就和原来没什么区别了。还是Angular的老问题,对象引用不变,成员变,无法触发子组件的ngOnChange。

所以,Signals也没有宣传中吹的天花乱坠那么厉害,解决的问题有限,它更像是一个Rxjs的一个封装,一个别名,一个简化版本。是不是要重构代码就看大家自己的需求了。

PS:Angular在开发signal component和双向绑定的signal,都在他们的任务列表中,等这两个发布可能会带来更多的好处,

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1335746.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

自定义注解 - java文档生成、结合SpringBoot使用

参考资料&#xff1a; 参考视频 拦截器自定义注解 AOP自定义注解 通过AOP获取属性 拦截器、过滤器、AOP的区别和联系 个人学习笔记及源码 注&#xff1a;这里仅讲怎么使用&#xff0c;具体原理请参考个人学习笔记 自定义注解源码介绍&#xff1a; 其中视频例子2为上述参…

智能优化算法应用:基于减法平均算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于减法平均算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于减法平均算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.减法平均算法4.实验参数设定5.算法结果6.…

[C/C++]数据结构 希尔排序

&#x1f966;前言: 希尔排序也称 “缩小增量排序”&#xff0c;它也是一种插入类排序的方法,在学习希尔排序之前我们首先了解一下直接插入排序. 一: &#x1f6a9;直接插入排序 1.1 &#x1f31f;排序思路 直接插入排序的基本原理是将一条记录插入到已排好的有序表中&#x…

微信小程序开发系列-01创建一个最小的小程序项目

本文讲述了通过微信开发者工具&#xff0c;创建一个新的小程序项目&#xff0c;完全从零开始&#xff0c;不依赖开发者工具的模板。目的是为了更好的理解小程序工程项目的构成。 文章目录 创建一个空项目app.json全局配置pagessitemapLocation app.js 创建一个空项目 打开微信…

Android 11.0 系统默认打开OEM解锁开关功能实现

1.前言 在11.0的系统rom定制化开发中,在9.0系统以后为了设备的安装,系统开始启用oem机制,所以在adb push文件就需要先oem解锁,然后才可以 进行相关操作,所以就需要默认打开oem解锁的开关,来方便oem解锁功能的实现 如图: 2.系统默认打开OEM解锁开关功能实现的核心类 pac…

大数据Doris(四十一):物化视图简单介绍

文章目录 物化视图简单介绍 一、适用场景

Apache Commons Math: 面向Java的数学和统计库

第1章&#xff1a;引言 大家好&#xff0c;我是小黑&#xff0c;咱们今天要聊的是Apache Commons Math这个宝藏级的Java库。为啥说它是宝藏呢&#xff1f;因为它简直就是处理数学问题的瑞士军刀&#xff0c;无论你是要搞统计分析、数值计算&#xff0c;还是解决优化问题&#…

微信小程序备案流程整理

一、备案流程 [找备案入口]–[填主体信息]–[填小程序信息]–[初审]–[短信核验]–[通管局审核] 1&#xff0c;在小程序后台找到备案入口 &#xff08;1&#xff09;新的未上架小程序&#xff0c;可以在小程序首页点击【去备案】进入。 &#xff08;2&#xff09;已上架小程…

智能优化算法应用:基于袋獾算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于袋獾算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于袋獾算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.袋獾算法4.实验参数设定5.算法结果6.参考文献7.MA…

【Midjourney】Midjourney提示词格式详解

目录 &#x1f347;&#x1f347;Midjourney是什么&#xff1f; &#x1f349;&#x1f349;Midjourney怎么用&#xff1f; &#x1f514;&#x1f514;Midjourney提示词格式 &#x1f341; 1.模型版本提示词&#x1f341; 参数 参数详解 应用示例 &#x1f343; 2.风格…

小狐狸GPT付费2.4.9 去除授权弹窗版

后台安装步骤&#xff1a; 1、在宝塔新建个站点&#xff0c;php版本使用7.2 、 7.3 或 7.4&#xff0c;把压缩包上传到站点根目录&#xff0c;运行目录设置为/public 2、导入数据库文件&#xff0c;数据库文件是 /db.sql 3、修改数据库连接配置&#xff0c;配置文件是/.env 4、…

三列布局 css

实现如下图的三列布局&#xff1a; .box {width:1400px;margin:0 auto;padding-bottom:40px;> .left {float:left;width:180px;margin-top:100px;text-align:center;}> .center {float:left;margin-top:100px;margin-left:130px;item-box {float:left;text-align:left;…

RPC(6):RMI实现RPC

1RMI简介 RMI(Remote Method Invocation) 远程方法调用。 RMI是从JDK1.2推出的功能&#xff0c;它可以实现在一个Java应用中可以像调用本地方法一样调用另一个服务器中Java应用&#xff08;JVM&#xff09;中的内容。 RMI 是Java语言的远程调用&#xff0c;无法实现跨语言。…

蓝牙物联网通信网络设计方案

随着当前经济的快速发展&#xff0c;社会运行节奏加快&#xff0c;人们更倾向于选择高效的出行方式&#xff0c;而飞机就是其中之一。近年来&#xff0c;全国各地机场的吞吐量不断增长&#xff0c;导致航站楼面积过大&#xff0c;而 GPS全球定位系统在室内感测不到卫星信号无法…

Java开发框架和中间件面试题(5)

44.Tomcat一个请求的处理流程&#xff1f; 假设来自客户的请求为&#xff1a; http&#xff1a;//localhost&#xff1a;8080/test/index.jsp请求被发送到本机端口8080&#xff0c;被在那里侦听Copote HTTP/1.1 Connector,然后 1.Connector把该请求交给它所在的Service的Engi…

(GCC) 库的操作

文章目录 预备静态库生成链接环境区别 动态库生成链接环境区别 END参考ar指令 预备 准备两个文件&#xff0c;以最简单的形式进行展示。 add.c int add(int x, int y) {return x y; }main.c 为了方便直接在头文件中声明函数 #include <stdio.h>extern int add(int,…

Python电能质量扰动信号分类(三)基于Transformer的一维信号分类模型

目录 引言 1 数据集制作与加载 1.1 导入数据 1.2 制作数据集 2 Transformer分类模型和超参数选取 2.1 定义Transformer分类模型 2.2 定义模型参数 3 Transformer模型训练与评估 3.1 模型训练 3.2 模型评估 代码、数据如下&#xff1a; 往期精彩内容&#xff1a; 电…

Leetcode—86.分隔链表【中等】

2023每日刷题&#xff08;六十九&#xff09; Leetcode—86.分隔链表 实现代码 /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/ struct ListNode* partition(struct ListNode* head, int x) {struct ListNode…

实验室安全教育考试管理系统v3.0功能介绍

瑞熙贝通实验室安全练习和在线考试系统&#xff0c;采取线上培训学习与安全考试相结合的教学形式&#xff0c;在学生进入开放实验室之前通过系统对实验的安全与规范有一个系统的认识与学习。通过线上考试系统&#xff0c;为评价学生的实验室安全学习效果提供了快速有效的实验平…

【bug】uniapp一键登录,自定义协议条款是否支持内部路由?

uniapp一键登录的自定义协议条款&#xff0c;不支持内部路由跳转 在uniapp文档上搜一键登录 加二维码之后可以提问