最近作公司新項目用的angular7,中碰到了一個很頭疼的問題在綁定對象中的數據改變時,頁面視圖沒有跟新,需點擊頁面中的時間元素後纔會更新。之前使用angularJs也常常碰到相似狀況,這種時候通常的方式使使用髒檢查(Dirty checking)讓angularJs檢查綁定到視圖上的數據來實現對頁面數據的刷新。api
接觸angular7時間還不久,angular高版本中提供了一系列組件的生命週期鉤子,能在組件生命週期的各個階段,對組件進行操做,監測組件中輸入數據的變化,基本少不多碰到須要用到相似於angularJs中髒檢查的狀況。一直覺得在高版本中的angular中已經不會出現須要髒檢查的狀況了,但是該來的早晚會來。app
在項目中因爲使用了公司的本身的播放插件,angular自身的事件註冊機制無效,只能使用原生的js位視頻播放插件註冊點擊事件。實現點擊插件後經過插件綁定的回調函數將該分屏信息發送給父組件,在父組件中經過對父組件綁定對象中的屬性值的改變來改變父組件的樣式。 框架
// 插件中的代碼片斷 ngAfterViewInit() { // 頁面視圖初始化完成以後爲視頻播放插件添加點擊事件
this.videoObj = document.getElementById('obj' + this.screen.id); this.videoObj.addEventListener('click', () => { this.selectThisScreen(this.screen); }, false); } private selectThisScreen(screen) { // 輸出該分屏對象,輸出給父組件
this.screenSelected.emit(screen); } // 父組件中的代碼片斷
private selectedScreen(screen) { this.videoLayoutService.selectedScreen(screen, this.videoLayoutOutput); } // videoLayoutService 片斷
this.screensConfig.screens.map(item => { if (item.id === screen.id) { item.selected = !item.selected; // 這是想要更新的值!!
if (item.selected) { videoLayoutOutput.emit({ device: { id: screen.device.Id, code: screen.device.SerialNo, selectedCamera: screen.device.SelectedCamera } }); } else { videoLayoutOutput.emit({ device: { id: null, code: null, selectedCamera: null } }); } } else if (item.id !== screen.id) { item.selected = false; } });
但是因爲插件事件綁定只支持原生js的方式,沒有有使用angular自身的事件綁定機制(已測試,在不適用該插件的條件下,使用angular自身的事件綁定機制view層可以監測到數據的跟新),view層沒能監測到綁定數據的變化。在嘗試了各類方式,和各類生命週期鉤子後(甚至測試了ngDoCheck)都沒有效果後都沒能達到想要的效果,這時我想會不會如今angular7中也存在和angularJs中數據更新視圖沒更新的同樣問題,若是是那也應該有相似與$scope.$applay()這樣相似的方式來解決這個問題。異步
而後去查了一下官網文檔,在文檔中沒有直接找到相似方法。後去網上找,後來在篇文章中發現了NgZone這個服務(具體哪一篇忘記記錄了,找的博客太多了後來忘記了),而後去官網查了查用法。async
官網文檔中使這樣描述的:An injectable service for executing work inside or outside of the Angular zone.(用於在Angular區域內外執行工做的可注入服務。)看的雲裏霧裏。。而後又看下面的說明。ide
The most common use of this service is to optimize performance when starting a work consisting of one or more asynchronous tasks that don't require UI updates or error handling to be handled by Angular. Such tasks can be kicked off via runOutsideAngular and if needed, these tasks can reenter the Angular zone via run.函數
大概是說這個服務最多見的用途是在啓動一個或多個異步組成工做是優化性能時使用,這些任務不禁angular處理UI更新或錯誤處理(感動終於找到問題所在了!由於插件使用原生js進行事件綁定,沒有走angular自身的事件綁定機制致使了angular框架不處理UI更新。。因此須要在這裏使用NgZone這個服務),這些任務能夠經過runOutsideAngular啓動,若是須要,這些任務能夠經過run從新進入Angular區域。性能
也就是說我如今可使用NgZone服務的提供的run()方法將這個事件加入到angular的UI更新管理區域中,一切引刃而解。測試
在父組件中註冊NgZone服務使用run()方法優化
// 父組件中的代碼片斷
constructor(private videoLayoutService: VideoLayoutService, private zone: NgZone) {
}
private selectedScreen(screen) { this.zone.run(() => { this.videoLayoutService.selectedScreen(screen, this.videoLayoutOutput); }); }
最後來簡單說明下NgZone中的幾個方法的使用
run():在Angular區域內同步執行fn函數,並返回函數返回的值。經過run運行函數容許您從在Angular區域以外執行的任務(一般經過runOutsideAngular啓動)從新進入Angular區域。在這個函數中調度的任何將來任務或微任務都將在Angular區域中繼續執行。若是發生同步錯誤,它將被從新拋出,而不是經過onError報告。
runTask():以任務的形式在Angular區域內同步執行fn函數,並返回函數返回的值。經過run運行函數容許您從在Angular區域以外執行的任務(一般經過runOutsideAngular啓動)從新進入Angular區域。在這個函數中調度的任何將來任務或微任務都將在Angular區域中繼續執行。若是發生同步錯誤,它將被從新拋出,而不是經過onError報告。
runGuarded() :與 run() 相同,除了同步錯誤是經過onError捕獲和轉發的,而不是從新拋出。
runOutsideAngular() : 在Angular的父區同步執行fn函數,並返回函數返回的值。經過runOutsideAngular運行函數可讓您逃離Angular的區域,並作一些不會觸發Angular變化檢測或受制於Angular錯誤處理的工做。在這個函數中調度的任何將來任務或微任務都將在Angular區域以外繼續執行。使用run從新進入Angular區域並執行更新應用程序模型的工做。