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
值变化,会不会触发子组件的变更检测和重新渲染。
答案是不会,ngModule
是 Angular
中用于定义模块的装饰器函数,它通常在应用程序的根模块中使用,并且在启动时就确定了,它的变化不会被视为触发变更检测的条件。
子组件input
框的 ngModule
值发生变化时,并不能直接触发子组件的变更检测,那么 input
框中内容会发生变化吗?
当 input
框的 ngModule
值发生变化时,输入框中的内容仍然会根据新的值进行更新,因为这是由 ngModel
实现的。这种更新是通过 DOM 事件(如 input
或 change
)来触发的,而不是通过 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()
方法可以标记组件及其父组件,使其在下一次变更检测周期中进行变更检测。
总结起来,通过 ChangeDetectorRef
的 detach()
方法,我们可以关闭变更检测以提高性能,并使用 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,通过一个 click 事件跳转到一个新页面,新页面迟迟不加载; 经过多次测试发现,将鼠标移入某个 tab ,页面就加载出来了。 举个例子,页面内容无法加载,但是将鼠标移入下图…...

Redis之String类型
文章目录 Redis之String类型1. 赋值/获取值2. 同时设置/获取多个键值3. 数值增减4. 获取字符串长度5. 向尾部追加值6. 分布式锁7.应用场景 Redis之String类型 Redis命令不区分大小写 1. 赋值/获取值 赋值:set key value 取值:get key (当键不存在时候&…...
使用redis中的zset实现滑动窗口限流
使用redis和zset实现滑动窗口限流 文章目录 使用redis和zset实现滑动窗口限流Zset**初始化一个ZSet**:其中包含所有用户的ID和时间戳。**添加元素到ZSet**:当用户发起请求时,将当前时间戳和用户ID作为元素添加到ZSet中。**删除过期的元素**&a…...

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

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

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

面试官:请说说flex布局_番茄出品.md
面试官:请说说flex布局_番茄出品.md start 依然记得当初学习 flex 布局时,用 flex 布局:画麻将。一筒到九筒,应有尽有。但是光和面试官说,我用 flex 布局画过麻将,并没有什么用。面试官问你一个语法&…...
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++)
由于之前写过打家劫舍系列,这里直接弄个合集,后面应该还有个iv。 目录 198. 打家劫舍 213. 打家劫舍 II 337. 打家劫舍 III 2560. 打家劫舍 IV 198. 打家劫舍 题目描述: 你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都…...
容易对一个异性产生依赖感怎么办?
歌词:爱总让人伤心,但你要学会去明白~ 👂 Photograph - Ed Sheeran - 单曲 - 网易云音乐 目录 🌼前言 😟一、对另一个人的依赖感,本质是什么? 😊二、如何减少对伴侣的依赖感&am…...

Windows10/11无线网卡WIFI驱动详细下载安装教程
官网下载WIFI驱动 《intel官网》 找到下载Windows 10 and Windows 11* WiFi package drivers 查看详细信息 下载对应操作系统的WIFI驱动 安装驱动,然后重启电脑即可。...

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

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

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

计算机竞赛 大数据商城人流数据分析与可视化 - python 大数据分析
0 前言 🔥 优质竞赛项目系列,今天要分享的是 🚩 基于大数据的基站数据分析与可视化 该项目较为新颖,适合作为竞赛课题方向,学长非常推荐! 🥇学长这里给一个题目综合评分(每项满分5分) 难度…...

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

人脸图像数据增强
为什么要做数据增强 在计算机视觉相关任务中,数据增强(Data Augmentation)是一种常用的技术,用于扩展训练数据集的多样性。它包括对原始图像进行一系列随机或有规律的变换,以生成新的训练样本。数据增强的主要目的是增…...
Android 查看按键信息的常用命令详解
Android 查看按键信息的常用命令详解 文章目录 Android 查看按键信息的常用命令详解一、主要命令:二、命令详解1、getevent2、getevent -l3、dumsys input4、cat XXX.kl4、cat /dev/input/eventX5、getevent 其他命令6、input keyevent XX 三、简单示例修改四、总结…...

【Java 基础篇】Properties 结合集合类的使用详解
Java 中的 Properties 类是一个常见的用于管理配置信息的工具,它可以被看作是一种键值对的集合。虽然 Properties 通常用于处理配置文件,但它实际上也可以作为通用的 Map 集合来使用。在本文中,我们将详细探讨如何使用 Properties 作为 Map 集…...
数字孪生体标准编程
数字孪生体标准 括ISO TC184/SC4正在制定数字孪生制造标准ISO 23247、ISO/IEC JTC1/AG11正在推动数字孪生体标准、IEEE P2806正在做有关“数字表达”的标准。赢家通吃的标准战 卡尔夏皮罗和哈尔范里安撰写了《信息规则:网络经济战略指南》(Information R…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式
一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...
Android Wi-Fi 连接失败日志分析
1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分: 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析: CTR…...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄
文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...
Go 语言并发编程基础:无缓冲与有缓冲通道
在上一章节中,我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道,它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好࿰…...
腾讯云V3签名
想要接入腾讯云的Api,必然先按其文档计算出所要求的签名。 之前也调用过腾讯云的接口,但总是卡在签名这一步,最后放弃选择SDK,这次终于自己代码实现。 可能腾讯云翻新了接口文档,现在阅读起来,清晰了很多&…...

三分算法与DeepSeek辅助证明是单峰函数
前置 单峰函数有唯一的最大值,最大值左侧的数值严格单调递增,最大值右侧的数值严格单调递减。 单谷函数有唯一的最小值,最小值左侧的数值严格单调递减,最小值右侧的数值严格单调递增。 三分的本质 三分和二分一样都是通过不断缩…...

基于PHP的连锁酒店管理系统
有需要请加文章底部Q哦 可远程调试 基于PHP的连锁酒店管理系统 一 介绍 连锁酒店管理系统基于原生PHP开发,数据库mysql,前端bootstrap。系统角色分为用户和管理员。 技术栈 phpmysqlbootstrapphpstudyvscode 二 功能 用户 1 注册/登录/注销 2 个人中…...

elementUI点击浏览table所选行数据查看文档
项目场景: table按照要求特定的数据变成按钮可以点击 解决方案: <el-table-columnprop"mlname"label"名称"align"center"width"180"><template slot-scope"scope"><el-buttonv-if&qu…...
在树莓派上添加音频输入设备的几种方法
在树莓派上添加音频输入设备可以通过以下步骤完成,具体方法取决于设备类型(如USB麦克风、3.5mm接口麦克风或HDMI音频输入)。以下是详细指南: 1. 连接音频输入设备 USB麦克风/声卡:直接插入树莓派的USB接口。3.5mm麦克…...