您好,登錄后才能下訂單哦!
這篇文章主要介紹“Angular中的變化檢測實例分析”的相關(guān)知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強(qiáng),希望這篇“Angular中的變化檢測實例分析”文章能幫助大家解決問題。
變化檢測是前端框架中很有趣的一部分內(nèi)容,各個前端的框架也都有自己的一套方案,一般情況下我們不太需要過多的了解變化檢測,因為框架已經(jīng)幫我們完成了大部分的工作。不過隨著我們深入的使用框架,我們會發(fā)現(xiàn)我們很難避免的要去了解變化檢測,了解變化檢測可以幫助我們更好的理解框架、排查錯誤、進(jìn)行性能優(yōu)化等等。
簡單的來說,變化檢測就是通過檢測視圖與狀態(tài)之間的變化,在狀態(tài)發(fā)生了變化后,幫助我們更新視圖,這種將視圖和我們的數(shù)據(jù)同步的機(jī)制就叫變化檢測。
我們了解了什么是變化檢測,那何時觸發(fā)變化檢測呢?我們可以看看下面這兩個簡單的Demo
Demo1:
一個計數(shù)器組件,點擊按鈕Count會一直加 1
@Component({ selector: "app-counter", template: ` Count:{{ count }} <br /> <button (click)="increase()">Increase</button> `, }) export class CounterComponent { count = 0; constructor() {} increase() { this.count = this.count + 1; } }
Demo2:
一個Todo List的組件,通過Http獲取數(shù)據(jù)后渲染到頁面
@Component({ selector: "app-todos", template: ` <li *ngFor="let item of todos">{{ item.titme }}</li> `, }) export class TodosComponent implements OnInit { public todos: TodoItem[] = []; constructor(private http: HttpClient) {} ngOnInit() { this.http.get<TodoItem[]>("/api/todos").subscribe((todos: TodoItem[]) => { this.todos = todos; }); } }
從上面的兩個 Demo 中我們發(fā)現(xiàn),在兩種情況下觸發(fā)了變化檢測:
點擊事件發(fā)生時
通過 http 請求遠(yuǎn)程數(shù)據(jù)時
仔細(xì)思考下,這兩種觸發(fā)的方式有什么共同點呢? 我們發(fā)現(xiàn)這兩種方式都是異步操作,所以我們可以得出一個結(jié)論: 只要發(fā)生了異步操作,Angular 就會認(rèn)為有狀態(tài)可能發(fā)生變化了,然后就會進(jìn)行變化檢測。
這個時候可能大家會想到 setTimeout
setInterval
,是的,它們同樣也會觸發(fā)變化檢測。
@Component({ selector: "app-counter", template: ` Count:{{ count }} <br /> <button (click)="increase()">Increase</button> `, }) export class CounterComponent implements OnInit { count = 0; constructor() {} ngOnInit(){ setTimeout(()=>{ this.count= 10; }); } increase() { this.count = this.count + 1; } }
簡而言之,如果發(fā)生以下事件之一,Angular 將觸發(fā)變化檢測:
任何瀏覽器事件(click、keydown 等)
setInterval()
和 setTimeout()
HTTP 通過 XMLHttpRequest
進(jìn)行請求
剛才我們了解到,只要發(fā)生了異步操作,Angular 就會進(jìn)行變化檢測,那 Angular 又是如何訂閱到異步事件的狀態(tài),從而觸發(fā)變化檢測的呢?這里我們就要聊一聊 zone.js 了。
Zone.js
Zone.js 提供了一種稱為 ** 區(qū)域(Zone) ** 的機(jī)制,用于封裝和攔截瀏覽器中的異步活動、它還提供 異步生命周期的鉤子 和 統(tǒng)一的異步錯誤處理機(jī)制。
Zone.js 是通過 Monkey Patching(猴子補(bǔ)?。?/strong> 的方式來對瀏覽器中的常見方法和元素進(jìn)行攔截,例如 setTimeout
和 HTMLElement.prototype.onclick
。Angular 在啟動時會利用 zone.js 修補(bǔ)幾個低級瀏覽器 API,從而實現(xiàn)異步事件的捕獲,并在捕獲時間后調(diào)用變化檢測。
下面用一段簡化的代碼來模擬一下替換 setTimeout 的過程:
function setTimeoutPatch() { // 存儲原始的setTimeout var originSetTimeout = window['setTimeout']; // 對瀏覽器原生方法的包裹封裝 window.setTimeout = function () { return global['zone']['setTimeout'].apply(global.zone, arguments); }; // 創(chuàng)建包裹方法,提供給上面重寫后的setTimeout使用? Zone.prototype['setTimeout'] = function (fn, delay) { // 先調(diào)用原始方法 originSetTimeout.apply(window, arguments); // 執(zhí)行完原始方法后就可以做其他攔截后需要進(jìn)行的操作了 ... }; }
NgZone
Zone.js 提供了一個全局區(qū)域,可以被 fork 和擴(kuò)展以進(jìn)一步封裝/隔離異步行為,Angular 通過創(chuàng)建一個fork并使用自己的行為擴(kuò)展它,通常來說, 在 Angular APP 中,每個 Task 都會在 Angular 的 Zone 中運(yùn)行,這個 Zone 被稱為 NgZone
。一個 Angular APP 中只存在一個 Angular Zone, 而變更檢測只會由運(yùn)行于這個 ** **NgZone**
** 中的異步操作觸發(fā) 。
簡單的理解就是: Angular 通過 Zone.js 創(chuàng)建了一個自己的區(qū)域并稱之為 NgZone,Angular 應(yīng)用中所有的異步操作都運(yùn)行在這個區(qū)域中。
我們了解 Angular 的核心是 組件化 ,組件的嵌套會使得最終形成一棵 組件樹 。
Angular 在生成組件的同時,還會為每一個組件生成一個變化檢測器 changeDetector
,用來記錄組件的數(shù)據(jù)變化狀態(tài),由于一個 Component 會對應(yīng)一個 changeDetector
,所以changeDetector
同樣也是一個樹狀結(jié)構(gòu)的組織。
在組件中我們可以通過注入 ChangeDetectorRef
來獲取組件的 changeDetector
@Component({ selector: "app-todos", ... }) export class TodosComponent{ constructor(cdr: ChangeDetectorRef) {} }
我們在創(chuàng)建一個 Angular 應(yīng)用 后,Angular 會同時創(chuàng)建一個 ApplicationRef
的實例,這個實例代表的就是我們當(dāng)前創(chuàng)建的這個 Angular 應(yīng)用的實例。 ApplicationRef
創(chuàng)建的同時,會訂閱 ngZone 中的 onMicrotaskEmpty
事件,在所有的微任務(wù)完成后調(diào)用所有的視圖的detectChanges()
來執(zhí)行變化檢測。
下是簡化的代碼:
class ApplicationRef { // ViewRef 是繼承于 ChangeDetectorRef 的 _views: ViewRef[] = []; constructor(private _zone: NgZone) { this._zone.onMicrotaskEmpty.subscribe({ next: () => { this._zone.run(() => { this.tick(); }); }, }); } // 執(zhí)行變化檢測 tick() { for (let view of this._views) { view.detectChanges(); } } }
單向數(shù)據(jù)流
什么是單向數(shù)據(jù)流?
剛才我們說了每次觸發(fā)變化檢測,都會從根組件開始,沿著整棵組件樹從上到下的執(zhí)行每個組件的變更檢測,默認(rèn)情況下,直到最后一個葉子 Component 組件完成變更檢測達(dá)到穩(wěn)定狀態(tài)。在這個過程中,一但父組件完成變更檢測以后,在下一次事件觸發(fā)變更檢測之前,它的子孫組件都不允許去更改父組件的變化檢測相關(guān)屬性狀態(tài)的,這就是單向數(shù)據(jù)流。
我們看一個示例:
@Component({ selector: "app-parent", template: ` {{ title }} <app-child></app-child> `, }) export class ParentComponent { title = "我的父組件"; } @Component({ selector: "app-child", template: ``, }) export class ChildComponent implements AfterViewInit { constructor(private parent: ParentComponent) {} ngAfterViewInit(): void { this.parent.title = "被修改的標(biāo)題"; } }
為什么出現(xiàn)這個錯誤呢?
這是因為我們違反了單向數(shù)據(jù)流,ParentComponent 完成變化檢測達(dá)到穩(wěn)定狀態(tài)后,ChildComponent 又改變了 ParentComponent 的數(shù)據(jù)使得 ParentComponent 需要再次被檢查,這是不被推薦的數(shù)據(jù)處理方式。在開發(fā)模式下,Angular 會進(jìn)行二次檢查,如果出現(xiàn)上述情況,二次檢查就會報錯: ExpressionChangedAfterItHasBeenCheckedError ,在生產(chǎn)環(huán)境中,則只會執(zhí)行一次檢查。
并不是在所有的生命周期去調(diào)用都會報錯,我們把剛才的示例修改一下:
@Component({ selector: "app-child", template: ``, }) export class ChildComponent implements OnInit { constructor(private parent: ParentComponent) {} ngOnInit(): void { this.parent.title = "被修改的標(biāo)題"; } }
修改后的代碼運(yùn)行正常,這是為什么呢?這里要說一下Angular檢測執(zhí)行的順序:
更新所有子子組件綁定的屬性
調(diào)用所有子組件生命周期的鉤子 OnChanges, OnInit, DoCheck ,AfterContentInit
更新當(dāng)前組件的DOM
調(diào)用子組件的變換檢測
調(diào)用所有子組件的生命周期鉤子 ngAfterViewInit
ngAfterViewInit
是在變化檢測之后執(zhí)行的,在執(zhí)行變化檢測后我們更改了父組件的數(shù)據(jù),在Angular執(zhí)行開發(fā)模式下的第二次檢查時,發(fā)現(xiàn)與上一次的值不一致,所以報錯,而ngOnInit
的執(zhí)行在變化檢測之前,所以一切正常。
這里提一下AngularJS,AngularJS采用的是雙向數(shù)據(jù)流,錯綜復(fù)雜的數(shù)據(jù)流使得它不得不多次檢查,使得數(shù)據(jù)最終趨向穩(wěn)定。理論上,數(shù)據(jù)可能永遠(yuǎn)不穩(wěn)定。AngularJS的策略是,臟檢查超過10次,就認(rèn)為程序有問題,不再進(jìn)行檢查。
剛才我們聊了變化檢測的工作流程,接下來我想說的是變化檢測的性能, 默認(rèn)情況下,當(dāng)我們的組件中某個值發(fā)生了變化觸發(fā)了變化檢測,那么Angular會從上往下檢查所有的組件。 不過Angular對每個組件進(jìn)行更改檢測的速度非??欤驗樗梢允褂?內(nèi)聯(lián)緩存 在幾毫秒內(nèi)執(zhí)行數(shù)千次檢查,其中內(nèi)聯(lián)緩存可生成對 VM 友好代碼。
盡管 Angular 進(jìn)行了大量優(yōu)化,但是遇到了大型應(yīng)用,變化檢測的性能仍然會下降,所以我們還需要用一些其他的方式來優(yōu)化我們的應(yīng)用。
Angular 提供了兩種運(yùn)行變更檢測的策略:
Default
OnPush
Default 策略
默認(rèn)情況下,Angular 使用 ChangeDetectionStrategy.Default
變更檢測策略,每次事件觸發(fā)變化檢測(如用戶事件、計時器、XHR、promise 等)時,此默認(rèn)策略都會從上到下檢查組件樹中的每個組件。這種對組件的依賴關(guān)系不做任何假設(shè)的保守檢查方式稱為 臟檢查 ,這種策略在我們應(yīng)用組件過多時會對我們的應(yīng)用產(chǎn)生性能的影響。
OnPush 策略
Angular 還提供了一種 OnPush
策略,我們可以修改組件裝飾器的 changeDetection
來更改變化檢測的策略
@Component({ selector: 'app-demo', // 設(shè)置變化檢測的策略 changeDetection: ChangeDetectionStrategy.OnPush, template: ... }) export class DemoComponent { ... }
設(shè)置為 OnPush 策略后,Angular 每次觸發(fā)變化檢測后會跳過該組件和該組件的所以子組件變化檢測
OnPush模式下變化檢測流程
在 OnPush
策略下,只有以下這幾種情況才會觸發(fā)組件的變化檢測:
輸入值(@Input)更改
當(dāng)前組件或子組件之一觸發(fā)了事件
手動觸發(fā)變化檢測
使用 async 管道后, observable 值發(fā)生了變化
在默認(rèn)的變更檢測策略中,Angular 將在 @Input()
數(shù)據(jù)發(fā)生更改或修改時執(zhí)行變化檢測,使用該 OnPush
時,傳入 @Input()
的值 必須是一個新的引用 才會觸發(fā)變化檢測。
JavaScript有兩種數(shù)據(jù)類型,值類型和引用類型,值類型包括:number、string、boolean、null、undefined,引用類型包括:Object、Arrary、Function,值類型每次賦值都會分配新的空間,而引用類型比如Object,直接修改屬性是引用是不會發(fā)生變化的,只有賦一個新的對象才會改變引用。
var a= 1; var b = a; b = 2; console.log(a==b); // false var obj1 = {a:1}; var obj2 = obj1; obj2.a = 2; console.log(obj1); // {a:2} console.log(obj1 === obj2); //true obj2= {...obj1}; console.log(obj1 === obj2); //false
如果 OnPush
組件或其子組件之一觸發(fā)事件,例如 click,則將觸發(fā)變化檢測(針對組件樹中的所有組件)。
需要注意的是在 OnPush
策略中,以下操作不會觸發(fā)變化檢測:
setTimeout()
setInterval()
Promise.resolve().then()
this.http.get('...').subscribe()
有三種手動觸發(fā)更改檢測的方法:
**detectChanges(): ** 它會觸發(fā)當(dāng)前組件和子組件的變化檢測
markForCheck(): 它不會觸發(fā)變化檢測,但是會把當(dāng)前的OnPush組件和所以的父組件為OnPush的組件 ** 標(biāo)記為需要檢測狀態(tài)** ,在當(dāng)前或者下一個變化檢測周期進(jìn)行檢測
ApplicationRef.tick() : 它會根據(jù)組件的變化檢測策略,觸發(fā)整個應(yīng)用程序的更改檢測
可以通過 在線Demo ,更直觀的了解這幾種觸發(fā)變化檢測的方式
內(nèi)置的 AsyncPipe
訂閱一個 observable 并返回它發(fā)出的最新值。
每次發(fā)出新值時的內(nèi)部 AsyncPipe
調(diào)用 markForCheck
private _updateLatestValue(async: any, value: Object): void { if (async === this._obj) { this._latestValue = value; this._ref.markForCheck(); } }
剛才我們聊了變化檢測的策略,我們可以使用
OnPush
的策略來優(yōu)化我們的應(yīng)用,那么這就夠了嗎? 在我們實際的開發(fā)中還會有很多的場景,我們需要通過一些其他的方式來繼續(xù)優(yōu)化我們的應(yīng)用。
場景1:
假如我們在實現(xiàn)一個回車搜索的功能:
@Component({ selector: "app-enter", template: `<input #input type="text" />`, }) export class EnterComponent implements AfterViewInit { @ViewChild("input", { read: ElementRef }) private inputElementRef: any; constructor() {} ngAfterViewInit(): void { this.inputElementRef.nativeElement.addEventListener( "keydown", (event: KeyboardEvent) => { const keyCode = event.which || event.keyCode; if (keyCode === 13) { this.search(); } } ); } search() { // ... } }
大家從上面的示例中可以發(fā)現(xiàn)什么問題呢?
我們知道事件會觸發(fā)Angular的變化檢測,在示例中綁定 keydown 事件后,每一次鍵盤輸入都會觸發(fā)變化檢測,而這些變化檢測大多數(shù)都是多余的檢測,只有當(dāng)按鍵為 Enter 時,才需要真正的進(jìn)行變化檢測。
在這種情況下,我們就可以利用 NgZone.runOutsideAngular()
來減少變化檢測的次數(shù)。
@Directive({ selector: '[enter]' }) export class ThyEnterDirective implements OnInit { @Output() enter = new EventEmitter(); constructor(private ngZone: NgZone, private elementRef: ElementRef<HTMLElement>) {} ngOnInit(): void { // 包裹代碼將運(yùn)行在Zone區(qū)域之外 this.ngZone.runOutsideAngular(() => { this.elementRef.nativeElement.addEventListener('keydown', (event: KeyboardEvent) => { const keyCode = event.which || event.keyCode; if (keyCode === 13) { this.ngZone.run(() => { this.enter.emit(event); }); } }); }); } }
場景2:
假如我們使用 WebSocket 將大量數(shù)據(jù)從后端推送到前端,則相應(yīng)的前端組件應(yīng)僅每 10 秒更新一次。在這種情況下,我們可以通過調(diào)用 detach()
和手動觸發(fā)它來停用更改檢測detectChanges()
:
constructor(private cdr: ChangeDetectorRef) { cdr.detach(); // 停用變化檢測 setInterval(() => { this.cdr.detectChanges(); // 手動觸發(fā)變化檢測 }, 10 * 1000); }
當(dāng)然使用 ngZone.runOutsideAngular()
也可以處理這種場景。
之前我們說了Angular 可以自動幫我們進(jìn)行變化檢測,這主要是基于Zone.js來實現(xiàn),那么很多人潛意識會任務(wù)Zone.js 就是 Angular 是一部分,Angular的 應(yīng)用程序必須基于Zone.js,其實不然,如果我們對應(yīng)用有極高的性能要求時,我們可以選擇移除 Zone.js,移除Zone.js 將會提升應(yīng)用的性能和打包的體積,不過帶來的后果就是我們需要主要去調(diào)用變化檢測。
如何移除 Zone.js?
手動調(diào)用變化檢測
在 Ivy 之后,我們有一些新的API可以更方便的調(diào)用變化檢測
**?markDirty: ** 標(biāo)記一個組件為 dirty 狀態(tài) (需要重新渲染) 并將在未來某個時間點安排一個變更檢測
?detectChanges: 因為某些效率方面的原因,內(nèi)部文檔不推薦使用 ?detectChanges
而推薦使用 ?markDirty
, ?detectChanges
會觸發(fā)組件以子組件的變更檢測。
移除后的性能
移除Zone.js后變化檢測由應(yīng)用自己來控制,極大的減少了不必要的變化檢測次數(shù),同時打包后的提及也減少了 36k
移除前:
移除后:
組件綁定
我們先來看一個組件綁定的例子:
按我們正常開發(fā)組件的想法,當(dāng)看到這個示例的時候一定認(rèn)為這個Case是Ok的,但是在運(yùn)行測試后我們發(fā)現(xiàn)這個Case失敗了。
在生產(chǎn)環(huán)境中,當(dāng) Angular 創(chuàng)建一個組件,就會自動進(jìn)行變更檢測。 但是在測試中,**TestBed.createComponent()**
并不會進(jìn)行變化檢測,需要我們手動觸發(fā)。
修改一下上面的Case:
origin-url0.00KB
origin-url0.00KB
從上面的示例中可以了解到,我們必須通過調(diào)用 fixture.detectChanges()
來告訴 TestBed 執(zhí)行數(shù)據(jù)綁定。
如果我們在測試中動態(tài)改變了綁定值,同樣也需要調(diào)用 fixture.detectChanges()
。
it("should update title", () => { component.title = 'Test Title'; fixture.detectChanges(); const h2 = fixture.nativeElement.querySelector("h2"); expect(h2.textContent).toContain('Test Title'); });
自動變更檢測
我們發(fā)現(xiàn)寫測試過程中需要頻繁的調(diào)用 fixture.detectChanges()
,可能會覺得比較繁瑣,那 Angular 可不可以在測試環(huán)境中自動運(yùn)行變化檢測呢?
我們可以通過配置 ComponentFixtureAutoDetect
來實現(xiàn)
TestBed.configureTestingModule({ declarations: [ BannerComponent ], providers: [ { provide: ComponentFixtureAutoDetect, useValue: true } ] });
然后再回頭看看剛才的示例:
上面的示例我們并沒有調(diào)用 fixture.detectChanges()
,但是測試依然通過了,這是因為我們開啟了自動變化檢測。
再看一個示例:
上面的示例中,我們在測試代碼中動態(tài)修改了 title 的值,測試運(yùn)行失敗,這是因為 Angular 并不知道測試改變了組件, ComponentFixtureAutoDetect
只對異步操作進(jìn)行自動變化檢測,例如 Promise、setTimeout、click 等DOM事件等,如果我們手動更改了綁定值,我們依然還需要調(diào)用 fixture.detectChanges()
來執(zhí)行變化檢測。
常見的坑
上面這個示例,綁定值修改后調(diào)用了 fixture.detectChanges()
, 但是運(yùn)行測試后仍然報錯,這是為什么呢?
查看Angular源碼后我們發(fā)現(xiàn) ** ngModel 的值是通過異步更新的** ,執(zhí)行fixture.detectChanges()
后雖然觸發(fā)了變化檢測,但是值還并未修改成功。
修改一下測試:
修改后我們將斷言包裹在了 fixture.whenStable()
中,然后測試通過,那 whenStable()
是什么呢?
whenStable(): Promise : 當(dāng)夾具穩(wěn)定時解析的承諾 當(dāng)事件已觸發(fā)異步活動或異步變更檢測后,可用此方法繼續(xù)執(zhí)行測試。
當(dāng)然除了用 fixture.whenStable()
我們也可以用 tick()
來解決這個問題
tick() :為 fakeAsync Zone 中的計時器模擬異步時間流逝 在此函數(shù)開始時以及執(zhí)行任何計時器回調(diào)之后,微任務(wù)隊列就會耗盡
上面這個示例,我們在修改屬性后調(diào)用了 fixture.detectChanges()
,但是測試未通過,這是為什么呢?我們發(fā)現(xiàn)這個示例與第一個示例唯一的區(qū)別就是這個組件是一個 OnPush
組件,之前我們說過默認(rèn)變化檢測會跳過 OnPush
組件的,只有在特定的幾種情況下才會觸發(fā)變化檢測的,遇到這種情況如何解決呢?
我們可以手動獲取組件的 ChangeDetectorRef
來主動觸發(fā)變化檢測。
虛擬DOM與增量DOM
Angular Ivy 是一個新的 Angular 渲染器,它與我們在主流框架中看到的任何東西都截然不同,因為它使用增量 DOM。 增量DOM是什么呢?它與虛擬Dom有什么不同呢?
虛擬 DOM
首先說一下虛擬DOM,我們要了解在瀏覽器中,直接操作Dom是十分損耗性能的,而虛擬DOM 的主要概念是將 UI的虛擬表示保存在內(nèi)存中,通過 Diff 操作對比當(dāng)前內(nèi)存和上次內(nèi)存中視圖的差異,從而減少不必要的Dom操作,只針對差異的Dom進(jìn)行更改。
虛擬DOM執(zhí)行流程:
當(dāng) UI 發(fā)生變化時,將整個 UI 渲染到 Virtual DOM 中。
計算先前和當(dāng)前虛擬 DOM 表示之間的差異。
使用更改更新真實的 DOM。
虛擬 DOM 的優(yōu)點:
高效的 Diff 算法。
簡單且有助于提高性能。
沒有 React 也可以使用
足夠輕量
允許構(gòu)建應(yīng)用程序且不考慮狀態(tài)轉(zhuǎn)換
增量Dom的主要概念是將組件編譯成一系列的指令,這些指令去創(chuàng)建DOM樹并在數(shù)據(jù)更改時就地的更新它們。
例如:
@Component({ selector: 'todos-cmp', template: ` <p *ngFor="let t of todos|async"> {{t.description}} </p> ` }) class TodosComponent { todos: Observable<Todo[]> = this.store.pipe(select('todos')); constructor(private store: Store<AppState>) {} }
編譯后:
var TodosComponent = /** @class */ (function () { function TodosComponent(store) { this.store = store; this.todos = this.store.pipe(select('todos')); } TodosComponent.ngComponentDef = defineComponent({ type: TodosComponent, selectors: [["todos-cmp"]], factory: function TodosComponent_Factory(t) { return new (t || TodosComponent)(directiveInject(Store)); }, consts: 2, vars: 3, template: function TodosComponent_Template(rf, ctx) { if (rf & 1) { // create dom pipe(1, "async"); template(0, TodosComponent_p_Template_0, 2, 1, null, _c0); } if (rf & 2) { // update dom elementProperty(0, "ngForOf", bind(pipeBind1(1, 1, ctx.todos))); } }, encapsulation: 2 }); return TodosComponent; }());
增量DOM的優(yōu)點:
渲染引擎可以被Tree Shakable,降低編譯后的體積
占用較低的內(nèi)存
為什么可渲染引擎可以被 Tree Shakable?
Tree Shaking 是指在編譯目標(biāo)代碼時移除上下文中未引用的代碼 ,增量 DOM 充分利用了這一點,因為它使用了基于指令的方法。正如示例所示,增量 DOM 在編譯之前將每個組件編譯成一組指令,這有助于識別未使用的指令。在 Tree Shakable 過程中,可以將這些未使用的的指令刪除掉。
減少內(nèi)存的使用
與虛擬 DOM 不同,增量 DOM 在重新呈現(xiàn)應(yīng)用程序 UI 時不會生成真實 DOM 的副本。此外,如果應(yīng)用程序 UI 沒有變化,增量 DOM 就不會分配任何內(nèi)存。大多數(shù)情況下,我們都是在沒有任何重大修改的情況下重新呈現(xiàn)應(yīng)用程序 UI。因此,按照這種方法可以極大的減少設(shè)備內(nèi)存使用。
關(guān)于“Angular中的變化檢測實例分析”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識,可以關(guān)注億速云行業(yè)資訊頻道,小編每天都會為大家更新不同的知識點。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。