当前位置: 首页 > Web前端 > HTML

学习笔记

时间:2023-03-29 11:57:24 HTML

Angular变化检测Angular变化检测机制比AngularJs中的等效机制更透明,更容易推理。但在某些情况下(比如做性能优化的时候),我们确实需要知道幕后发生了什么。因此,让我们通过以下主题更深入地探讨变更检测:如何实施变更检测?Angular变化检测器是什么样子的,我能看到它吗?默认的变更检测机制是如何工作的打开/关闭变更检测并手动触发它以避免变更检测循环:生产模式vs开发模式什么是OnPush变更检测模式?使用Immutable.js简化Angular应用程序的构建如何实施变更检测?Angular可以检测组件数据何时发生更改,并自动重新呈现视图以反映该更改。但是它如何在页面上任何地方都可能发生的低级别事件(例如按钮单击)之后执行此操作?要理解这是如何工作的,我们需要首先认识到Javascript中的整个运行时是设计为可重新加载的。如果需要,我们可以覆盖原生函数,如String或Number。覆盖浏览器默认机制当Angular应用程序启动时,它会修补几个低级浏览器API,例如addEventListener,这是一个用于注册所有浏览器事件的浏览器函数,包括点击处理程序。Angular将其替换为另一个新版本的addEventListener://这是新版本的addEventListenerfunctionaddEventListener(eventName,callback){//调用真正的addEventListenercallRealAddEventListener(eventName,function(){//首先调用原始回调callback(...);//然后运行特定于Angular的功能varchanged=angular.runChangeDetection();if(changed){angular.reRenderUIPart();}});新版本的addEventListener添加了更多功能:不仅调用已注册的回调,而且Angular有机会运行更改检测并更新UI。这个低级运行时补丁操作是如何工作的?浏览器API的这种低级修补是由Angular附带的名为Zone.js的库完成的。了解什么是区域很重要。区域只不过是在JavascriptVM的多轮执行中存活下来的执行上下文。这是我们可以用来向浏览器添加额外功能的通用机制。Angular在内部使用Zones来触发变化检测,但另一种可能的用途是应用程序分析,或跟踪跨多个VM轮次运行的长堆栈跟踪。浏览器支持异步API修补以下常见浏览器机制以支持更改检测:所有浏览器事件(单击、鼠标悬停、按键等)setTimeout()和setInterval()AjaxHTTP请求事实上,Zone.js修补许多其他浏览器API以透明地触发Angular变化检测,例如Websockets。这种机制的一个限制是,如果出于某种原因Zone.js不支持异步浏览器API,则不会触发更改检测。这解释了变化检测是如何被触发的,但是它一旦被触发又是如何工作的呢?变更检测树每个Angular组件都有一个关联的变更检测器,它是在应用程序启动时创建的。这是一个例子:@Component({selector:'todo-item',template:`{{todo.owner.firstname}}-{{todo.description}}-已完成:{{todo.completed}}`})exportclassTodoItem{@Input()todo:Todo;@Output()toggle=newEventEmitter();onToggle(){this.toggle.emit(this.todo);该组件将接收一个Todo对象作为输入,并在切换todo状态时发出一个事件。为了使示例更有趣,Todo类包含一个嵌套对象:exportclassTodo{constructor(publicid:number,publicdescription:string,publiccompleted:boolean,publicowner:Owner){}}设置在代码中Todo类A断点:当上面第11行被触发时,我们观察调试器中的上下文:默认的变更检测机制是如何工作的?这个方法一开始可能看起来很奇怪,所有的变量名。但是通过深入研究,我们注意到它做了一些非常简单的事情:对于模板中使用的每个表达式,它将表达式中使用的属性的当前值与该属性的先前值进行比较。如果前后的属性值不同,isChanged会被设置为true,就是这个原理。事实上,它通过使用一种称为looseNotIdentical()的方法来比较值,这实际上只是一个===与针对NaN情况的特殊逻辑的比较。源代码如下:那么嵌套对象所有者呢?我们在变化检测器代码中可以看到,owner嵌套对象的属性也在进行变化检测。但只比较名字属性,而不比较姓氏属性。这是因为组件模板中没有使用lastname属性。同样,不比较Todo的顶级id属性。默认情况下,AngularChangeDetection通过检查模板表达式的值是否已更改来工作。这是为所有组件完成的。此外,Angular不会进行深入的对象比较来检测变化,它只考虑模板使用的属性。OnPush变化检测模式如果我们的Todo列表变得非常大,我们可以将TodoList组件配置为仅在Todo列表发生变化时更新自身。这可以通过将组件更改检测策略更新为OnPush来完成:@Component({selector:'todo-list',changeDetection:ChangeDetectionStrategy.OnPush,template:...})exportclassTodoList{...}现在让我们添加应用程序的几个按钮:一个通过直接更改列表的第一项来切换它,另一个将待办事项添加到整个列表。代码如下所示:@Component({selector:'app',template:`
切换第一项AddTodotoList`})exportclassApp{todos:Array=initialData;constructor(){}toggleFirst(){this.todos[0].completed=!这个.todos[0].完成;}addTodo(){让newTodos=this.todos.slice(0);newTodos.push(newTodo(1,"TODO4",false,newOwner("John","Doe")));this.todos=newTodos;}}测试结果:第一个按钮“Togglefirstitem”不起作用!这是因为toggleFirst()方法直接更改列表的一个元素。TodoList无法检测到这一点,因为它的输入引用todos没有改变并且第二个按钮能够工作。请注意,方法addTodo()创建了待办事项列表的副本,然后向副本添加了一个项目,最后用复制的列表替换了todos成员变量。这会触发更改检测,因为该组件在其输入中检测到引用更改:它收到一个新列表。当使用OnPush检测器时,当OnPush组件的任何输入属性发生变化、触发事件或Observable触发事件时,框架将对OnPush组件执行变化检测。Angular变化检测的一个重要特征是,与AngularJs不同,它强制执行单向数据流:当我们的控制器类上的数据更新时,变化检测运行并更新视图。但是,视图本身的更新不会触发进一步的更改。假设这些由视图更新触发的进一步更新,又会触发对视图的进一步更新,这就是AngularJs中所谓的摘要循环。总结Angular变化检测是一种内置的框架特性,可确保组件数据与其HTML模板视图之间的自动同步。变更检测的工作原理是检测常见的浏览器事件,例如鼠标点击、HTTP请求和其他类型的事件,并决定每个组件的视图是否需要更新。变化检测有两种类型:默认变化检测:Angular通过比较事件前后的所有模板表达式值来决定视图是否需要更新。OnPush变化检测:它通过检测一些新数据是否已经通过组件输入或使用异步管道订阅的Observable显式推送到组件中来工作。Angular的默认变化检测机制实际上与AngularJs非常相似:它比较浏览器事件前后模板表达式的值,以查看是否发生了变化。它为所有组件执行此操作。但也有一些重要的区别:一方面,没有变化检测循环,也没有AngularJs中命名的摘要循环。这允许仅通过查看其模板和控制器来推理每个组件。另一个区别是,由于变化检测器的构建方式,检测组件变化的机制要快得多。最后,与AngularJs不同的是,变更检测机制是可定制的。更多Jerry原创文章在这里:《王子熙》: