框架 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>

总结

但愿官方能够关注这个问题,并及时解决。

版权声明

本文做者: 河北工业大学梦云智开发团队 - 张喜硕
相关文章
相关标签/搜索