引言
使用NG
初始化一个简单的Hello World!
的Sample
。
渲染完成后的DOM
如下所示:
<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>
被保留,用于包裹组件模板内容。
当然这种设计是没问题的,因为上级可能给组件加属性,但是如果在某些情况下会很难处理。
<div class="page">
<app-subject-preview></app-subject-preview>
</div>
还是试题预览的需求,循环遍历试题,使用了试题预览组件,试题预览渲染后的DOM
是这样的。
<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
如下,没有组件调用的那一层,易于处理:
<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
Github
上2017
年04
月25
日有人提出了该问题,希望和ng-container
一样,认为组件的标签应该是可选的,可渲染成注释,不应该强制显示。至今官方未解决。
Components host-element should (optional) be a html-comment instead of html-element - Github
问题的讨论中提出了几个方案,使用ViewContainerRef
,经测试无效。
一位老哥发帖参考了React
,提出了Fragment
的方案,个人觉得该方案非常好,但官方迟迟未采纳。
隔壁的 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>
DOM
渲染结果:
<body>
<div id="app">
<p>Hello World!</p>
</div>
</body>
这种渲染结果算是比较理想的,但组件必须存在一个根标签也会造成额外的问题,强制组件有根标签也会造成多一层的问题。
将组件的p
标签去掉,会产生如下错误:
思考
二者在创建组件时都需要组件拥有根元素,Angular
是保留了原组件标签作为根元素,Vue
是强制组件声明一个根标签作为根元素,两者实现类似。
我没有阅读过Angular
与Vue
的框架源码,但从官方迟迟未解决该问题,且多款框架都采用类似方式实现来推测,应该与检测算法有关,多个根元素可能不利于检测算法的实现。
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>
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>
总结
希望官方可以关注这个问题,并及时解决。
版权声明
本文作者:河北工业大学梦云智开发团队 - 张喜硕
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。