当前位置: 首页 > news >正文

Angular变更检测机制

前段时间遇到这样一个 bug,通过一个 click 事件跳转到一个新页面,新页面迟迟不加载;
经过多次测试发现,将鼠标移入某个 tab ,页面就加载出来了。

举个例子,页面内容无法加载,但是将鼠标移入下图的 消息 或者 历史 tab,页面就加载出来了。

在这里插入图片描述

正常情况下,Angular 会在 组件初始化完毕后 自动进行变更检测,并更新视图。

但有时候,你可能希望在 组件实例化后 立即执行一次变更检测,以确保视图能够及时更新。这种情况下,我们可以在构造函数之后的代码中显式调用 detectChanges() 方法。

为了在切换路由后立即进行 变更检测 并渲染新页面,在路由导航结束后,Angular 提供了一个 Router.events 事件流来监听路由导航的状态。开发者可以订阅 NavigationEnd 事件,并在回调函数中调用 detectChanges() 方法。

import { Component, OnInit, ChangeDetectorRef } from '@angular/core';
import { Router, NavigationEnd } from '@angular/router';@Component({// 组件元数据
})
export class MyComponent implements OnInit {constructor(private router: Router,private cdr: ChangeDetectorRef,) {}ngOnInit() {this.router.events.subscribe(event => {if (event instanceof NavigationEnd) {this.cdr.detectChanges(); // 在路由切换完成后调用 detectChanges}});}}

如此,就实现了跳转页面视图不展示的问题。

这里,简单介绍一下 Angular 的变更检测策略,包括 默认策略OnPush 策略无策略

要关闭变更检测,可以将变更检测策略设置为 无策略ChangeDetectionStrategy.OnPush)或 手动调用 detach() 方法来分离变更检测器。

1 变更检测策略

1.1 手动分离变更检测器

使用 detach() 方法来分离变更检测器,这样组件就不再与变更检测关联。

import { Component, OnInit, ChangeDetectorRef } from '@angular/core';@Component({selector: 'app-my-component',templateUrl: './my-component.component.html'
})
export class MyComponent implements OnInit {constructor(private cdr: ChangeDetectorRef) {}ngOnInit() {this.cdr.detach(); // 分离变更检测器}
}

在此方法中,我们需要将 ChangeDetectorRef 注入到组件中,并在 ngOnInit 生命周期钩子函数中调用 detach() 方法。这样就会分离变更检测器,从而关闭变更检测。

1.2 使用变更检测策略

将组件的变更检测策略设置为 ChangeDetectionStrategy.OnPush,这会使得组件仅在输入属性发生变化时才进行变更检测。我们可以在组件的元数据中指定变更检测策略。

import { Component, ChangeDetectionStrategy } from '@angular/core';@Component({selector: 'app-my-component',templateUrl: './my-component.component.html',changeDetection: ChangeDetectionStrategy.OnPush // 设置变更检测策略为 OnPush
})
export class MyComponent {}

使用此策略时,我们需要手动触发变更检测,例如通过注入 ChangeDetectorRef 并调用 detectChanges() 方法。

需要注意的是,关闭变更检测可能导致视图无法及时更新,因此应仔细考虑是否真正需要关闭变更检测。一般情况下,使用默认的变更检测策略并让 Angular 自动执行变更检测是推荐的做法。只有在特定的性能优化需求下才应该考虑手动关闭变更检测。

2 ChangeDetectionStrategy.OnPush 策略

这里,我们要注意,将组件的变更检测策略设置为 ChangeDetectionStrategy.OnPush,这会使得组件仅在 输入属性 发生变化时才进行变更检测。输入属性指的是通过 @Input 装饰器定义在组件上的属性。这些属性用于从父组件向子组件传递数据。

当将组件的变更检测策略设置为 ChangeDetectionStrategy.OnPush 时,组件只会在 输入属性 发生变化时才触发变更检测和重新渲染。

举个例子,MyComponent 的组件,定义了一个输入属性 data,只有每次 data 的值发生变化, Angular 才会自动触发变更检测并更新组件的视图。

import { Component, Input, ChangeDetectionStrategy } from '@angular/core';@Component({selector: 'app-my-component',template: `<div>{{ data }}</div>`,changeDetection: ChangeDetectionStrategy.OnPush // 设置变更检测策略为 OnPush
})
export class MyComponent {@Input() data: string;
}

通过将组件的变更检测策略设置为 ChangeDetectionStrategy.OnPush,我们可以确保只有父组件修改了子组件的输入属性或者父组件手动调用 markForCheck() 方法,子组件才会执行变更检测、重新渲染,如此可以提高性能并减少不必要的变更检测。

事实上,除了输入属性发生变化,组件自身的事件(例如按钮点击、定时器等),也可以触发变更检测;

注意,当只有子组件内部的变量值发生改变而没有其他触发条件满足时,ChangeDetectionStrategy.OnPush 策略下的组件不会自动触发变更检测。这意味着组件的视图不会被更新。

2.1 子组件 ngModule 值变化

这里,可能会有人提问,子组件的 ngModule 值变化,会不会触发子组件的变更检测和重新渲染。

答案是不会,ngModuleAngular 中用于定义模块的装饰器函数,它通常在应用程序的根模块中使用,并且在启动时就确定了,它的变化不会被视为触发变更检测的条件。

子组件input框的 ngModule 值发生变化时,并不能直接触发子组件的变更检测,那么 input 框中内容会发生变化吗?
input 框的 ngModule 值发生变化时,输入框中的内容仍然会根据新的值进行更新,因为这是由 ngModel 实现的。这种更新是通过 DOM 事件(如 inputchange)来触发的,而不是通过 Angular 的变更检测系统。

2.2 子组件 内部数据发生变化

这里,举一个内部数据发生变化的例子。

  • 使用 ChangeDetectionStrategy.OnPush 策略;

count 的值定时发生变化,但是页面并未更新;

import { ChangeDetectorRef, ChangeDetectionStrategy } from '@angular/core';@Component({selector: 'test',templateUrl: './test.component.html',styleUrls: ['./test.component.less'],changeDetection: ChangeDetectionStrategy.OnPush,
})export class TestComponent implements OnInit, OnDestroy {constructor(private router: Router,private cdr: ChangeDetectorRef,) { }count = 1;ngOnInit(): void {setInterval(() => {this.count++;}, 1000);}
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 不使用 ChangeDetectionStrategy.OnPush 策略;

页面数据会刷新

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

2.3 父组件传值发生变化

父组件传值,直接修改了可变对象(如:数组、对象等),而没有创建一个新的引用,那么组件可能无法检测到这个改变,从而不会触发变更检测。

items: string[] = ['Item 1', 'Item 2', 'Item 3'];
this.items.push('New Item');

3 关闭变更检测

Angular 中,我们可以通过使用 ChangeDetectorRef 来关闭或禁用变更检测。ChangeDetectorRef 是一个服务,提供了与变更检测相关的方法。以下是一种关闭变更检测的方法:

注入 ChangeDetectorRef 服务:

import { Component, ChangeDetectorRef } from '@angular/core';@Component({selector: 'app-example',template: `<!-- Your component's template -->`
})
export class ExampleComponent {constructor(private cdr: ChangeDetectorRef) {}
}

在需要关闭变更检测的地方调用 detach() 方法

this.cdr.detach();

调用 detach() 方法会将组件从变更检测树中分离,意味着该组件及其子组件将不再进行自动的变更检测和视图更新。这可以帮助减少不必要的计算和渲染,提高性能。

需要注意的是,一旦你将组件从变更检测树中分离,它将不再自动响应输入属性的变化或事件的触发。如果你需要手动控制变更检测,可以使用 markForCheck() 方法来通知 Angular 运行变更检测。例如:

this.cdr.markForCheck();

使用 markForCheck() 方法可以标记组件及其父组件,使其在下一次变更检测周期中进行变更检测。

总结起来,通过 ChangeDetectorRefdetach() 方法,我们可以关闭变更检测以提高性能,并使用 markForCheck() 方法手动触发变更检测。请注意,在大多数情况下,不需要手动关闭变更检测,Angular 的默认行为通常已经足够高效和准确。

this.cdr.markForCheck()this.cdr.detectChanges()是Angular中的变更检测相关方法,它们有一些区别和适用场景。

  • this.cdr.markForCheck(): 调用markForCheck()方法会通知 Angular 在下一次变更检测周期中检查组件及其子组件,并执行相应的变更检测操作。该方法将标记组件为“已脏”(dirty),意味着组件可能发生了变化,需要进行检查。这对于在组件中进行异步操作,但可能没有直接触发变更的情况非常有用。调用markForCheck()方法本身不会立即触发变更检测,而是等待下一次变更检测周期执行。

  • this.cdr.detectChanges(): 调用detectChanges()方法会立即触发一次变更检测,无论组件是否已被标记为“脏”。这将从组件树的根节点开始进行变更检测,并检查任何已标记为“脏”的组件。这可以用来强制立即进行变更检测,而不必等待下一次自动变更检测周期。但是,过度使用detectChanges()方法可能会导致性能问题,因为它执行了全面的变更检测,而不只是对标记为“脏”的组件进行检查。

从使用角度来看,通常情况下应该优先使用this.cdr.markForCheck()方法,因为它更高效且更符合 Angular 的变更检测机制。只有在特殊情况下,如在组件中调用了异步操作但未触发变更时,才应该使用this.cdr.detectChanges()方法来强制立即进行变更检测。

相关文章:

Angular变更检测机制

前段时间遇到这样一个 bug&#xff0c;通过一个 click 事件跳转到一个新页面&#xff0c;新页面迟迟不加载&#xff1b; 经过多次测试发现&#xff0c;将鼠标移入某个 tab &#xff0c;页面就加载出来了。 举个例子&#xff0c;页面内容无法加载&#xff0c;但是将鼠标移入下图…...

Redis之String类型

文章目录 Redis之String类型1. 赋值/获取值2. 同时设置/获取多个键值3. 数值增减4. 获取字符串长度5. 向尾部追加值6. 分布式锁7.应用场景 Redis之String类型 Redis命令不区分大小写 1. 赋值/获取值 赋值&#xff1a;set key value 取值&#xff1a;get key (当键不存在时候&…...

使用redis中的zset实现滑动窗口限流

使用redis和zset实现滑动窗口限流 文章目录 使用redis和zset实现滑动窗口限流Zset**初始化一个ZSet**&#xff1a;其中包含所有用户的ID和时间戳。**添加元素到ZSet**&#xff1a;当用户发起请求时&#xff0c;将当前时间戳和用户ID作为元素添加到ZSet中。**删除过期的元素**&a…...

Linux下C语言使用 netlink sockets与内核模块通信

netlink简介 Netlink套接字是用以实现用户进程与内核进程通信的一种特殊的进程间通信(IPC) ,也是网络应用程序与内核通信的最常用的接口。在Linux标准内核中&#xff0c;系统默认集成了很多netlink实例&#xff0c;比如日志上报、路由系统等&#xff0c;netlink消息是双向的&a…...

excel中的引用与查找函数篇3

1、INDEX(array,row_num,[col_num])&#xff1a;获取指定范围中指定行号和列号对应的数据 index(查询范围,行号,列号) 行号和列号是相对选中查询范围来写的&#xff1a;分别把第二行第三列的数据和第四行第二列的数据查找出来。 数据是单行或单列&#xff0c;后面只需要给一个参…...

【Linux学习笔记】 - 常用指令学习及其验证(下)

前言&#xff1a;本文延续上一篇文章【Linux学习笔记】 - 常用指令学习及其验证&#xff08;上&#xff09;对常用的指令进行介绍和验证。 一、mv指令 &#xff08;1&#xff09;功能&#xff1a;用来移动文件或者将文件改名 &#xff08;2&#xff09;语法及验证&#xff1a…...

面试官:请说说flex布局_番茄出品.md

面试官&#xff1a;请说说flex布局_番茄出品.md start 依然记得当初学习 flex 布局时&#xff0c;用 flex 布局&#xff1a;画麻将。一筒到九筒&#xff0c;应有尽有。但是光和面试官说&#xff0c;我用 flex 布局画过麻将&#xff0c;并没有什么用。面试官问你一个语法&…...

ChatGLM DeepSpeed/P-Tuning v2 调参

之前尝试了基于ChatGLM-6B使用LoRA进行参数高效微调,本文给大家分享使用DeepSpeed和P-Tuning v2对ChatGLM-6B进行微调,相关代码放置在GitHub上面:llm-action。 ChatGLM-6B简介 ChatGLM-6B相关的简介请查看之前的文章,这里不再赘述。 P-Tuning v2简介 P-Tuning是一种较新…...

Leetcode每日一题:打家劫舍系列Ⅰ、Ⅱ、Ⅲ、Ⅳ(2023.9.16~2023.9.19 C++)

由于之前写过打家劫舍系列&#xff0c;这里直接弄个合集&#xff0c;后面应该还有个iv。 目录 198. 打家劫舍 213. 打家劫舍 II 337. 打家劫舍 III 2560. 打家劫舍 IV 198. 打家劫舍 题目描述&#xff1a; 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都…...

容易对一个异性产生依赖感怎么办?

歌词&#xff1a;爱总让人伤心&#xff0c;但你要学会去明白~ &#x1f442; Photograph - Ed Sheeran - 单曲 - 网易云音乐 目录 &#x1f33c;前言 &#x1f61f;一、对另一个人的依赖感&#xff0c;本质是什么&#xff1f; &#x1f60a;二、如何减少对伴侣的依赖感&am…...

Windows10/11无线网卡WIFI驱动详细下载安装教程

官网下载WIFI驱动 《intel官网》 找到下载Windows 10 and Windows 11* WiFi package drivers 查看详细信息 下载对应操作系统的WIFI驱动 安装驱动&#xff0c;然后重启电脑即可。...

面向面试知识--Lottery项目

面向面试知识–Lottery项目 1.设计模式 为什么需要设计模式&#xff1f; &#xff08;设计模式是什么&#xff1f;优点有哪些&#xff1f;&#xff09; 设计模式是一套经过验证的有效的软件开发指导思想/解决方案&#xff1b;提高代码的可重用性和可维护性&#xff1b;提高团…...

SpringBoot接口中如何直接返回图片数据

SpringBoot接口中如何直接返回图片数据 目录 接口直接返回图片数据 起因 类似这种 根据个人经验 优雅的实现图片返回 接口直接返回图片数据 起因 最近在做涉及到分享推广的业务&#xff0c;需要由业务员分享二维码进入推广页面&#xff0c;由于是新项目&#xff0c;前期…...

c语言进阶部分详解(指针进阶1)

大家好&#xff01;指针的初阶内容我已经写好&#xff0c;可移步至我的文章&#xff1a;c语言进阶部分详解&#xff08;指针初阶&#xff09;_总之就是非常唔姆的博客-CSDN博客 基本内容我便不再赘述&#xff0c;直接带大家进入进阶内容&#xff1a; 目录 一.字符指针 1.讲解…...

计算机竞赛 大数据商城人流数据分析与可视化 - python 大数据分析

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于大数据的基站数据分析与可视化 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度…...

各种电机驱动原理

步进电机 步进电机参考资料 野火官方文档 步进电机驱动原理 上面参考文档中有的内容就不写了&#xff0c;写一下我自己的总结吧。 说明&#xff1a; 电机驱动器输入信号有电机转动方向信号DIR&#xff0c;电机转速信号PWM&#xff0c;电机使能信号EN&#xff1b;电机驱动器…...

人脸图像数据增强

为什么要做数据增强 在计算机视觉相关任务中&#xff0c;数据增强&#xff08;Data Augmentation&#xff09;是一种常用的技术&#xff0c;用于扩展训练数据集的多样性。它包括对原始图像进行一系列随机或有规律的变换&#xff0c;以生成新的训练样本。数据增强的主要目的是增…...

Android 查看按键信息的常用命令详解

Android 查看按键信息的常用命令详解 文章目录 Android 查看按键信息的常用命令详解一、主要命令&#xff1a;二、命令详解1、getevent2、getevent -l3、dumsys input4、cat XXX.kl4、cat /dev/input/eventX5、getevent 其他命令6、input keyevent XX 三、简单示例修改四、总结…...

【Java 基础篇】Properties 结合集合类的使用详解

Java 中的 Properties 类是一个常见的用于管理配置信息的工具&#xff0c;它可以被看作是一种键值对的集合。虽然 Properties 通常用于处理配置文件&#xff0c;但它实际上也可以作为通用的 Map 集合来使用。在本文中&#xff0c;我们将详细探讨如何使用 Properties 作为 Map 集…...

数字孪生体标准编程

数字孪生体标准 括ISO TC184/SC4正在制定数字孪生制造标准ISO 23247、ISO/IEC JTC1/AG11正在推动数字孪生体标准、IEEE P2806正在做有关“数字表达”的标准。赢家通吃的标准战 卡尔夏皮罗和哈尔范里安撰写了《信息规则&#xff1a;网络经济战略指南》&#xff08;Information R…...

CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型

CVPR 2025 | MIMO&#xff1a;支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题&#xff1a;MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者&#xff1a;Yanyuan Chen, Dexuan Xu, Yu Hu…...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解

【关注我&#xff0c;后续持续新增专题博文&#xff0c;谢谢&#xff01;&#xff01;&#xff01;】 上一篇我们讲了&#xff1a; 这一篇我们开始讲&#xff1a; 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下&#xff1a; 一、场景操作步骤 操作步…...

前端倒计时误差!

提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)

概述 在 Swift 开发语言中&#xff0c;各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过&#xff0c;在涉及到多个子类派生于基类进行多态模拟的场景下&#xff0c;…...

Go 语言接口详解

Go 语言接口详解 核心概念 接口定义 在 Go 语言中&#xff0c;接口是一种抽象类型&#xff0c;它定义了一组方法的集合&#xff1a; // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的&#xff1a; // 矩形结构体…...

linux 错误码总结

1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放

简介 前面两期文章我们介绍了I2S的读取和写入&#xff0c;一个是通过INMP441麦克风模块采集音频&#xff0c;一个是通过PCM5102A模块播放音频&#xff0c;那如果我们将两者结合起来&#xff0c;将麦克风采集到的音频通过PCM5102A播放&#xff0c;是不是就可以做一个扩音器了呢…...

pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)

目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关&#xff0…...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解

在 C/C 编程的编译和链接过程中&#xff0c;附加包含目录、附加库目录和附加依赖项是三个至关重要的设置&#xff0c;它们相互配合&#xff0c;确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中&#xff0c;这些概念容易让人混淆&#xff0c;但深入理解它们的作用和联…...

RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill

视觉语言模型&#xff08;Vision-Language Models, VLMs&#xff09;&#xff0c;为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展&#xff0c;机器人仍难以胜任复杂的长时程任务&#xff08;如家具装配&#xff09;&#xff0c;主要受限于人…...