当前位置: 首页 > 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…...

测试微信模版消息推送

进入“开发接口管理”--“公众平台测试账号”&#xff0c;无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息&#xff1a; 关注测试号&#xff1a;扫二维码关注测试号。 发送模版消息&#xff1a; import requests da…...

PHP和Node.js哪个更爽?

先说结论&#xff0c;rust完胜。 php&#xff1a;laravel&#xff0c;swoole&#xff0c;webman&#xff0c;最开始在苏宁的时候写了几年php&#xff0c;当时觉得php真的是世界上最好的语言&#xff0c;因为当初活在舒适圈里&#xff0c;不愿意跳出来&#xff0c;就好比当初活在…...

3.3.1_1 检错编码(奇偶校验码)

从这节课开始&#xff0c;我们会探讨数据链路层的差错控制功能&#xff0c;差错控制功能的主要目标是要发现并且解决一个帧内部的位错误&#xff0c;我们需要使用特殊的编码技术去发现帧内部的位错误&#xff0c;当我们发现位错误之后&#xff0c;通常来说有两种解决方案。第一…...

Debian系统简介

目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版&#xff…...

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...

AGain DB和倍数增益的关系

我在设置一款索尼CMOS芯片时&#xff0c;Again增益0db变化为6DB&#xff0c;画面的变化只有2倍DN的增益&#xff0c;比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析&#xff1a; 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...

Selenium常用函数介绍

目录 一&#xff0c;元素定位 1.1 cssSeector 1.2 xpath 二&#xff0c;操作测试对象 三&#xff0c;窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四&#xff0c;弹窗 五&#xff0c;等待 六&#xff0c;导航 七&#xff0c;文件上传 …...

逻辑回归暴力训练预测金融欺诈

简述 「使用逻辑回归暴力预测金融欺诈&#xff0c;并不断增加特征维度持续测试」的做法&#xff0c;体现了一种逐步建模与迭代验证的实验思路&#xff0c;在金融欺诈检测中非常有价值&#xff0c;本文作为一篇回顾性记录了早年间公司给某行做反欺诈预测用到的技术和思路。百度…...

day36-多路IO复用

一、基本概念 &#xff08;服务器多客户端模型&#xff09; 定义&#xff1a;单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用&#xff1a;应用程序通常需要处理来自多条事件流中的事件&#xff0c;比如我现在用的电脑&#xff0c;需要同时处理键盘鼠标…...

Oracle11g安装包

Oracle 11g安装包 适用于windows系统&#xff0c;64位 下载路径 oracle 11g 安装包...