成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

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

未東興 / 1980人閱讀

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

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

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

我最近在NgConf的一個(gè)研討會(huì)上討論了Angular中的高級(jí)DOM操作的話題。我從基礎(chǔ)知識(shí)開(kāi)始講起,例如使用模版引用和DOM查詢來(lái)訪問(wèn)DOM元素,一直談到了使用視圖容器來(lái)動(dòng)態(tài)渲染模版和組件。如果你還沒(méi)有看過(guò)這個(gè)演講,我鼓勵(lì)你去看看。通過(guò)一系列的實(shí)踐,你將可以快速地學(xué)會(huì)新知識(shí),并加強(qiáng)認(rèn)知。關(guān)于這個(gè)話題,我在NgViking 也有一個(gè)簡(jiǎn)單地談話。

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

你可以在這個(gè)GitHub倉(cāng)庫(kù)中找到我演講中使用過(guò)的樣例。

窺探視圖引擎

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

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

解決這個(gè)任務(wù)的一個(gè)錯(cuò)誤的方法就是使用Renderer或者原生的DOM API來(lái)直接移除 DOM 元素:

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

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

然而,如果你接著檢查一下控制臺(tái),Angular依然報(bào)道子組件的數(shù)量為1,而不是0。并且關(guān)于對(duì)子組件A及其子節(jié)點(diǎn)的變更檢測(cè)還在錯(cuò)誤的運(yùn)行著。這里是控制臺(tái)輸出的日志:

為什么?

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

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

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

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

視圖容器View Container

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

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

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

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

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

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

操控動(dòng)態(tài)視圖

在你開(kāi)始創(chuàng)建一個(gè)視圖并將其附加到視圖容器之前,你需要引入組件模版的容器并且將其進(jìn)行實(shí)例化。模版中的任何元素都可以充當(dāng)視圖容器,不過(guò),通常扮演這個(gè)角色的候選者是,因?yàn)樵谒鼤?huì)渲染成一個(gè)注釋節(jié)點(diǎn),所以不會(huì)給DOM帶來(lái)冗余的元素。

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

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

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

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

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

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

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

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

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

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

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

要?jiǎng)?chuàng)建一個(gè)宿主視圖,你就需要一個(gè)組件工廠。如果你需要了解Angular中動(dòng)態(tài)組件的話,點(diǎn)擊這里可以學(xué)習(xí)到更多關(guān)于組件工廠和動(dòng)態(tài)組件的知識(shí)。

在Angular中,我們可以使用componentFactoryResolver這個(gè)服務(wù)來(lái)獲取一個(gè)組件工廠的引用:

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

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

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

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

移除視圖

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

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

優(yōu)化技術(shù)

有時(shí)你需要重復(fù)地渲染和隱藏模版中定義好的相同組件或HTML。在下面這個(gè)例子中,通過(guò)點(diǎn)擊不同的按鈕,我們可以切換要顯示的組件:

如果我們把之前學(xué)過(guò)的知識(shí)簡(jiǎn)單地應(yīng)用一下,那代碼將會(huì)如下所示:

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

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

在這個(gè)例子中,宿主視圖會(huì)因?yàn)槲覀兪褂媒M件工廠和createComponent方法,而銷毀和重復(fù)創(chuàng)建。如果我們使用createEmbeddedView方法和TemplateRef,那嵌入視圖也會(huì)被銷毀和重復(fù)創(chuàng)建:

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

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

ViewRef

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

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

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

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

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

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

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

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

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

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

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

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

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

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/100341.html

相關(guān)文章

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

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

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

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

    CoderStudy 評(píng)論0 收藏0
  • 前端每周清單:Node.js 微服務(wù)實(shí)踐,Vue.js 與 GraphQL,Angular 組件技巧

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

    wall2flower 評(píng)論0 收藏0
  • 尋找真兇Echarts or Angular

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

    sumory 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<