框架 DOM 渲染問題探究

引言

使用NG初始化一個簡單的Hello World!Samplejavascript

image.png

image.png

image.png

渲染完成後的DOM以下所示:html

<app-root _nghost-ifx-c0="" ng-version="8.2.14">
  <app-hello-world _ngcontent-ifx-c0="" _nghost-ifx-c1="">
    <p _ngcontent-ifx-c1="">Hello World!</p>
  </app-hello-world>
</app-root>

根據渲染結果代表,渲染後的DOM中原組件節點<app-hello-world></app-hello-world>被保留,用於包裹組件模板內容。java

固然這種設計是沒問題的,由於上級可能給組件加屬性,可是若是在某些狀況下會很難處理。git

<div class="page">
  <app-subject-preview></app-subject-preview>
</div>

仍是試題預覽的需求,循環遍歷試題,使用了試題預覽組件,試題預覽渲染後的DOM是這樣的。github

<div class="page">
  <app-subject-preview>
    <p>完型填空大題幹</p>
    <p>31. A.xx B.xx C.xx D.xx</p>
    <p>32. A.xx B.xx C.xx D.xx</p>
    <p>33. A.xx B.xx C.xx D.xx</p>
    <p>34. A.xx B.xx C.xx D.xx</p>
  </app-subject-preview>
</div>

子節點獲取到的是一個個app-subject-preview,因分頁時高度計算調用十分頻繁,不易處理較繁重的計算任務,若是遞歸向下獲取直到沒有子元素時再處理,恐會產生性能問題。算法

期待生成的DOM以下,沒有組件調用的那一層,易於處理:app

<div class="page">
  <p>完型填空大題幹</p>
  <p>31. A.xx B.xx C.xx D.xx</p>
  <p>32. A.xx B.xx C.xx D.xx</p>
  <p>33. A.xx B.xx C.xx D.xx</p>
  <p>34. A.xx B.xx C.xx D.xx</p>
</div>

實現

Github Issue

Github20170425日有人提出了該問題,但願和ng-container同樣,認爲組件的標籤應該是可選的,可渲染成註釋,不該該強制顯示。至今官方未解決。框架

Components host-element should (optional) be a html-comment instead of html-element - Github性能

問題的討論中提出了幾個方案,使用ViewContainerRef,經測試無效。測試

一位老哥發帖參考了React,提出了Fragment的方案,我的以爲該方案很是好,但官方遲遲未採納。

image.png

隔壁的 Vue

去看了一下隔壁Vue的渲染方式(Vue規範中不推薦用;)。

掛載一個Vue應用:

var app = new Vue({
  el: '#app'
})

建立一個Vue組件:

Vue.component('helloWorld', {
  template: '<p>Hello World!</p>'
})

HTML頁面:

<body>
  <div id="app">
    <hello-world></hello-world>
  </div>
</body>

image.png

DOM渲染結果:

<body>
  <div id="app">
    <p>Hello World!</p>
  </div>
</body>

這種渲染結果算是比較理想的,但組件必須存在一個根標籤也會形成額外的問題,強制組件有根標籤也會形成多一層的問題。

將組件的p標籤去掉,會產生以下錯誤:

image.png

image.png

思考

兩者在建立組件時都須要組件擁有根元素,Angular是保留了原組件標籤做爲根元素,Vue是強制組件聲明一個根標籤做爲根元素,二者實現相似。

我沒有閱讀過AngularVue的框架源碼,但從官方遲遲未解決該問題,且多款框架都採用相似方式實現來推測,應該與檢測算法有關,多個根元素可能不利於檢測算法的實現。

ng-container

退而求其次,只能棄用組件的方式,手動經過ng-template/ng-container實現。

這裏我推薦使用ng-container來減小循環帶來的層級嵌套問題,使用方式與正常指令使用一致。

<ng-container *ngFor="let item of arr">
  {{ item }}
</ng-container>
<div *ngFor="let item of arr">
  {{ item }}
</div>

image.png

DOM渲染結果以下所示(全部的ng-container都被渲染爲註釋):

<app-root _nghost-ohv-c0="" ng-version="8.2.14">
  <!--bindings={
    "ng-reflect-ng-for-of": "0,1,2,3,4,5,6,7,8,9"
  }-->
    <!----> 0
    <!----> 1
    <!----> 2
    <!----> 3
    <!----> 4
    <!----> 5
    <!----> 6
    <!----> 7
    <!----> 8
    <!----> 9
  <!--bindings={
    "ng-reflect-ng-for-of": "0,1,2,3,4,5,6,7,8,9"
  }-->
  <div _ngcontent-ohv-c0="">0</div>
  <div _ngcontent-ohv-c0="">1</div>
  <div _ngcontent-ohv-c0="">2</div>
  <div _ngcontent-ohv-c0="">3</div>
  <div _ngcontent-ohv-c0="">4</div>
  <div _ngcontent-ohv-c0="">5</div>
  <div _ngcontent-ohv-c0="">6</div>
  <div _ngcontent-ohv-c0="">7</div>
  <div _ngcontent-ohv-c0="">8</div>
  <div _ngcontent-ohv-c0="">9</div>
</app-root>

總結

但願官方能夠關注這個問題,並及時解決。

版權聲明

本文做者: 河北工業大學夢雲智開發團隊 - 張喜碩
相關文章
相關標籤/搜索