国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

在Angular中操作DOM:意料之外的結(jié)果及優(yōu)化技術

未東興 / 1924人閱讀

摘要:翻譯在中操作意料之外的結(jié)果及優(yōu)化技術原文鏈接作者譯者而井我最近在的一個研討會上討論了中的高級操作的話題。首先,我會介紹在中操作的工具和方法,然后再介紹一些我在研討會上沒有說過的更高級的優(yōu)化技術。

【翻譯】在Angular中操作DOM:意料之外的結(jié)果及優(yōu)化技術

原文鏈接:https://blog.angularindepth.c...  
作者:Max Koretskyi
譯者:而井

我最近在NgConf的一個研討會上討論了Angular中的高級DOM操作的話題。我從基礎知識開始講起,例如使用模版引用和DOM查詢來訪問DOM元素,一直談到了使用視圖容器來動態(tài)渲染模版和組件。如果你還沒有看過這個演講,我鼓勵你去看看。通過一系列的實踐,你將可以快速地學會新知識,并加強認知。關于這個話題,我在NgViking 也有一個簡單地談話。

然而,如果你覺得那個版本太長了(譯者注:指演講視頻)不想看,或者比起聽,你更喜歡閱讀,那么我在這篇文章總結(jié)了(演講的)關鍵概念。首先,我會介紹在Angular中操作DOM的工具和方法,然后再介紹一些我在研討會上沒有說過的、更高級的優(yōu)化技術。

你可以在這個GitHub倉庫中找到我演講中使用過的樣例。

窺探視圖引擎

假設你有一個要將一個子組件從DOM中移除的任務。這里有一個父組件,它的模塊中有一個子組件A需要被移除:

@Component({
  ...
  template: `
    
    
  `
})
export class AppComponent {}

解決這個任務的一個錯誤的方法就是使用Renderer或者原生的DOM API來直接移除 DOM 元素:

@Component({...})
export class AppComponent {
  ...
  remove() {
    this.renderer.removeChild(
       this.hostElement.nativeElement, // parent App comp node
       this.childComps.first.nativeElement // child A comp node
     );
  }
}

你可以在這里看到整個解決方案(譯者注:樣例代碼)。如果你通過Element tab來審查移除節(jié)點之后的HTML結(jié)果,你將看到子組件A已經(jīng)不存在DOM中了。

然而,如果你接著檢查一下控制臺,Angular依然報道子組件的數(shù)量為1,而不是0。并且關于對子組件A及其子節(jié)點的變更檢測還在錯誤的運行著。這里是控制臺輸出的日志:

為什么?

發(fā)生這種情況是因為,在Angular內(nèi)部中,使用了通常稱為View或Component View的數(shù)據(jù)結(jié)構來代表組件。這張圖顯示了視圖和DOM之間的關系:

每個視圖都由持有對應DOM元素的視圖節(jié)點所組成。所以,當我們直接修改DOM的時候,視圖內(nèi)部的視圖節(jié)點以及持有的DOM元素引用并沒有被影響。這里有一張圖可以展示在我們從DOM中移除組件A后,DOM和視圖的狀態(tài):

并且由于所有的變更檢測操作和對子視圖的包含,都是運行在視圖中而不是DOM上,Angular檢測與組件相關的視圖,并且報告(譯者注:組件數(shù)量)為1,而不是我們期望的0。此外,由于與組件A相關的視圖依舊存在,所以對于組件A及其子組件的變更檢測操作依然會被執(zhí)行。

要正確地解決這個問題,我們需要一個能直接處理視圖的工具,在Angular中它就是視圖容器View Container。

視圖容器View Container

視圖容器可以保障DOM級別的變動的安全,在Angular中,它被所有內(nèi)置的結(jié)構指令所使用。在視圖內(nèi)部有一種特別的視圖節(jié)點類型,它扮演著其他視圖容器的角色:

正如你所見的那樣,它持有兩種類型的視圖:嵌入視圖(embedded views)和宿主視圖(host views)。

在Angular中只有這些視圖類型,它們(視圖)主要的不同取決于用什么輸入數(shù)據(jù)來創(chuàng)建它們。并且嵌入視圖只能附加(譯者注:掛載)到視圖容器中,而宿主視圖可以被附加到任何DOM元素上(通常稱其為宿主元素)。

嵌入視圖可以使用TemplateRef通過模版來創(chuàng)建,而宿主視圖得使用視圖(組件)工廠來創(chuàng)建。例如,用于啟動程序的主要組件AppComponent,在內(nèi)部被當作為一個用來附加掛載組件宿主元素的宿主視圖。

視圖容器提供了用來創(chuàng)建、操作和移除動態(tài)視圖的API。我稱它們?yōu)閯討B(tài)視圖,是為了和那些由框架在模版中發(fā)現(xiàn)的靜態(tài)組件所創(chuàng)建出來的靜態(tài)視圖做對比。Angular不會對靜態(tài)視圖使用視圖容器,而是在子組件特定的節(jié)點內(nèi)保持一個對子視圖的引用。這張圖可以表明這個想法:

正如你所見,這里沒有視圖容器,子視圖的引用是直接附加到組件A的視圖節(jié)點上的。

操控動態(tài)視圖

在你開始創(chuàng)建一個視圖并將其附加到視圖容器之前,你需要引入組件模版的容器并且將其進行實例化。模版中的任何元素都可以充當視圖容器,不過,通常扮演這個角色的候選者是,因為在它會渲染成一個注釋節(jié)點,所以不會給DOM帶來冗余的元素。

為了將任意元素轉(zhuǎn)化成一個視圖容器,我們需要對一個視圖查詢使用{read: ViewContainerRef} 配置:

@Component({
 …
 template: ``
})
export class AppComponent implements AfterViewChecked {
  @ViewChild("vc", {read: ViewContainerRef}) viewContainer: ViewContainerRef;
}

一旦Angular執(zhí)行對應的視圖查詢并將視圖容器的的引用賦值給一個類的屬性,你就可以使用這個引用來創(chuàng)建一個動態(tài)視圖了。

創(chuàng)建一個嵌入視圖

為了創(chuàng)建一個嵌入視圖,你需要一個模版。在Angular中,我們會使用 來包裹任意DOM元素和定義模版的結(jié)構。然后我們就可以簡單地用一個帶有 {read: TemplateRef} 參數(shù)的視圖查詢來獲取這個模版的引用:

@Component({
  ...
  template: `
    
        
    
  `
})
export class AppComponent implements AfterViewChecked {
    @ViewChild("tpl", {read: TemplateRef}) tpl: TemplateRef;
}

一旦Angular執(zhí)行這個查詢并且將模版的引用賦值給類的屬性后,我們就可以通過createEmbeddedView方法使用這個引用來創(chuàng)建和附加一個嵌入視圖到一個視圖容器中:

@Component({ ... })
export class AppComponent implements AfterViewInit {
    ...
    ngAfterViewInit() {
        this.viewContainer.createEmbeddedView(this.tpl);
    }
}

你需要在ngAfterViewInit生命周期中實現(xiàn)你的邏輯,因為視圖查詢是那時完成實例化的。而且你可以給模版(譯者注:嵌入視圖的模版)中的值綁定一個上下文對象(譯者注:即模版上綁定的值隸屬于這個上下文對象)。你可以通過查看API文檔來了解更多詳情。

你可以在這里找到創(chuàng)建嵌入視圖的整個樣例代碼。

創(chuàng)建一個宿主視圖

要創(chuàng)建一個宿主視圖,你就需要一個組件工廠。如果你需要了解Angular中動態(tài)組件的話,點擊這里可以學習到更多關于組件工廠和動態(tài)組件的知識。

在Angular中,我們可以使用componentFactoryResolver這個服務來獲取一個組件工廠的引用:

@Component({ ... })
export class AppComponent implements AfterViewChecked {
  ...
  constructor(private r: ComponentFactoryResolver) {}
  ngAfterViewInit() {
    const factory = this.r.resolveComponentFactory(ComponentClass);
  }
 }
}

一旦我們得到一個組件工廠,我們就可以用它來初始化組件,創(chuàng)建宿主視圖并將其視圖附加到視圖容器之上。為了達到這一步,我們只需簡單地調(diào)用createComponent方法,并且傳入一個組件工廠:

@Component({ ... })
export class AppComponent implements AfterViewChecked {
    ...
    ngAfterViewInit() {
        this.viewContainer.createComponent(this.factory);
    }
}

你可以在這里找到創(chuàng)建宿主視圖的樣例代碼。

移除視圖

一個視圖容器中的任何附加視圖,都可以通過removedetach方法來刪除。兩個方法都會將視圖從視圖容器和DOM中移除。但是remove方法會銷毀視圖,所以之后不能重新附加(譯者注:即從緩存中獲取再附加,不用重新創(chuàng)建),detach方法會保持視圖的引用,以便未來可以重新使用,這個對于我接下來要講的優(yōu)化技術很重要。

所以,為了正確地解決移除一個子組件或任意DOM元素這個問題,首先有必要創(chuàng)建一個嵌入視圖或宿主視圖,并將其附加到視圖容器上。然后你才有辦法使用任何可用的API方法來將視圖從視圖容器和DOM中移除。

優(yōu)化技術

有時你需要重復地渲染和隱藏模版中定義好的相同組件或HTML。在下面這個例子中,通過點擊不同的按鈕,我們可以切換要顯示的組件:

如果我們把之前學過的知識簡單地應用一下,那代碼將會如下所示:

@Component({...})
export class AppComponent {
  show(type) {
    ...
    // 視圖被銷毀
    this.viewContainer.clear();
    
    // 視圖被創(chuàng)建并附加到視圖容器之上   
    this.viewContainer.createComponent(factory);
  }
}

最終,我們會得一個不想要的結(jié)果:每當按鈕被點擊、show方法被執(zhí)行時,視圖都會被銷毀和重新創(chuàng)建。

在這個例子中,宿主視圖會因為我們使用組件工廠和createComponent方法,而銷毀和重復創(chuàng)建。如果我們使用createEmbeddedView方法和TemplateRef,那嵌入視圖也會被銷毀和重復創(chuàng)建:

show(type) {
    ...
    // 視圖被銷毀
    this.viewContainer.clear();
    // 視圖被創(chuàng)建并附加到視圖容器之上   
    this.viewContainer.createEmbeddedView(this.tpl);
}

理想狀況下,我們只需創(chuàng)建視圖一次,之后在我們需要的時候復用它。有一個視圖容器的API,它提供了將已經(jīng)存在的視圖附加到視圖容器之上、移除視圖卻不銷毀視圖的辦法。

ViewRef

ComponentFactoryTemplateRef都實現(xiàn)了用來創(chuàng)建視圖的創(chuàng)建方法。事實上,當你調(diào)用createEmbeddedViewcreateComponent 方法并傳入輸入數(shù)據(jù)時,視圖容器在底層內(nèi)部使用了這些創(chuàng)建方法。有一個好消息就是我們可以自己調(diào)用這些方法來創(chuàng)建一個嵌入或宿主視圖、獲取視圖的引用。在Angular中,視圖可以通過ViewRef及其子類型來引用。

創(chuàng)建一個宿主視圖

所以通過這樣,你可以使用一個組件工廠來創(chuàng)建一個宿主視圖和獲取它的引用:

aComponentFactory = resolver.resolveComponentFactory(AComponent);
aComponentRef = aComponentFactory.create(this.injector);
view: ViewRef = aComponentRef.hostView;

在宿主視圖情況下,視圖與組件的關聯(lián)(引用)可以通過ComponentRef調(diào)用create方法來獲取。通過一個hostView屬性來暴露。

一旦我們獲得到這個視圖,它就可以通過insert方法附加到一個視圖容器之上。另外一個你不想顯示的視圖可以通過detach方法來從視圖中移除并保持引用。所以可以通過這樣來解決組件切換顯示問題:

showView2() {
    ...
    //  視圖1將會從視圖容器和DOM中移除
    this.viewContainer.detach();
    // 視圖2將會被附加于視圖容器和DOM之上
    this.viewContainer.insert(view);
}

注意,我們使用detach方法來代替clearremove方法,為之后的復用保持視圖(的引用)。你可以在這里找到整個實現(xiàn)。

創(chuàng)建一個嵌入視圖

在以一個模版為基礎來創(chuàng)建一個嵌入視圖的情況下,視圖(引用)可以直接通過createEmbeddedView方法來返回:

view1: ViewRef;
view2: ViewRef;
ngAfterViewInit() {
    this.view1 = this.t1.createEmbeddedView(null);
    this.view2 = this.t2.createEmbeddedView(null);
}

與之前的例子類似,有一個視圖將會從視圖容器移除,另外一個視圖將會被重新附加到視圖容器之上。你可以在這里找到整個實現(xiàn)。

有趣的是,視圖容器(譯者注:ViewContainerRef類型)的createEmbeddedViewcreateComponent這兩個創(chuàng)建視圖的方法,都會返回被創(chuàng)建的視圖的引用。

文章版權歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/100341.html

相關文章

  • 前端每周清單第 11 期:Angular 4.1支持TypeScript 2.3,Vue 2.3優(yōu)化

    摘要:斯坦福宣布使用作為計算機課程的首選語言近日,某位有年教學經(jīng)驗的斯坦福教授決定放棄,而使用作為計算機入門課程的教學語言。斯坦福官方站點將它們新的課程描述為是最流行的構建交互式的開發(fā)語言,本課程會用講解中的實例。 前端每周清單第 11 期:Angular 4.1支持TypeScript 2.3,Vue 2.3優(yōu)化服務端渲染,優(yōu)秀React界面框架合集 為InfoQ中文站特供稿件,首發(fā)地址為...

    warkiz 評論0 收藏0
  • 前端每周清單第 34 期:Vue 現(xiàn)狀盤點與 3.0 展望,React 代碼遷移與優(yōu)化,圖片優(yōu)化詳論

    摘要:工程實踐立足實踐,提示實際水平內(nèi)聯(lián)函數(shù)與性能很多關于性能優(yōu)化的文章都會談及內(nèi)聯(lián)函數(shù),其也是常見的被詬病為拖慢性能表現(xiàn)的元兇之一不過本文卻是打破砂鍋問到底,論證了內(nèi)聯(lián)函數(shù)并不一定就會拖慢性能,過度的性能優(yōu)化反而會有損于應用性能。 showImg(https://segmentfault.com/img/remote/1460000011481413?w=1240&h=825); 前端每周...

    CoderStudy 評論0 收藏0
  • 前端每周清單:Node.js 微服務實踐,Vue.js 與 GraphQL,Angular 組件技巧

    摘要:前端每周清單第期微服務實踐,與,組件技巧,攻防作者王下邀月熊編輯徐川前端每周清單專注前端領域內(nèi)容,以對外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點分為新聞熱點開發(fā)教程工程實踐深度閱讀開源項目巔峰人生等欄目。 前端每周清單第 26 期:Node.js 微服務實踐,Vue.js 與 GraphQL,Angular 組件技巧,HeadlessChrome 攻防 作者:王下邀月熊 編輯:徐川...

    wall2flower 評論0 收藏0
  • 尋找真兇Echarts or Angular

    摘要:我們再來看一下調(diào)用棧,如下圖從圖中我們發(fā)現(xiàn)了一個調(diào)用棧的代碼執(zhí)行過,還記得里提到嗎發(fā)起臟檢查的通知者,它代理了原生事件,任何一個原生異步事件的觸發(fā)都會導致的運行。 尋找真兇Echarts or Angular 這是一篇故事,就如同技術,我們所追求的不是一個結(jié)局,而是那些深受啟發(fā)與共鳴的過程,那是我們成長的經(jīng)驗與生產(chǎn)力的積淀! 故事開始于瘋了的ionic3應用 頁面打開,什么也沒做5s里...

    sumory 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<