Angular是否使用DOM diffing或者必须"重新渲染"每个列表项?

时间:2015-03-16 00:38:24

标签: javascript html angularjs dom reactjs

我是Angular的新手,但我已经听过(和read)有关其渲染模型的一些“谣言”,以及它与React的不同之处。

我已经阅读了Angular专家的一些帖子,他们声称如果你必须使用Angular渲染长列表,它可能会很慢,因为如果有什么变化,Angular会重新渲染整个列表,而React(例如)“一旦它已经被渲染,它将不会从头开始重新渲染整个列表,但它将在内部跟踪渲染的DOM元素,并在新调用时创建新的虚拟DOM,将其与前一个进行比较并仅应用更改”

(引自一篇关于角度渲染问题的随机博客文章)

因此,当我开始学习Angular时,我的第一件事就是尝试这个。

似乎我无法重现这个问题......

这是一个虚拟的Plunker,我created来重现这个问题。

您可以将新项目添加到使用ng-repeat呈现的消息列表中,如下所示:

<table>
  <tr ng-repeat="m in messages" class="{{name}}">
    <td>{{m.message}}</td>
    <td>{{m.date}}</td>
  </tr>
</table>

您可以点击可以更新其中一个此类项目的按钮,然后您可以更新与该列表完全无关的其他属性(name

现在,如果我打开开发人员工具栏,并在表格中修改项目的HTML属性,然后点击“添加新消息”,我的修改不会被Angular丢失或覆盖 - 似乎Angular不会完全重新渲染DOM。它看起来很聪明。

Angular最近是否开始使用DOM差异? (我的演示使用Angular 1.4.0 beta)

仅仅因为从DOM渲染的角度来看,我只是没有看到React和Angular之间的巨大差异。

你能告诉我一个用例,它展示了Angular渲染模型的缺点吗?

1 个答案:

答案 0 :(得分:6)

关于这个话题存在很多混乱,主要是因为它并不是一个过于简单化的角度重新渲染所有东西&#34;输入答案。

角度数据绑定的基础(在1.x版本中)围绕着$digest循环和$scope的概念。 $scope是一个特殊的对象,它自我跟踪&#34;它的属性,并使用方法$scope.$watch()为每个属性创建一个JavaScript事件侦听器。这些侦听器监视HTML中可更改输入元素和$scope属性的更改。

每当任何JavaScript侦听器触发时,$digest循环都会遍历$watch下的每个项目,并相应地更新值。您也可以调用$scope.$apply()手动执行$digest循环。此循环可以多次执行,因为对一个值的更改可能会影响$watch下的另一个值,从而触发另一个$digest。但是,$digest循环确实有一个迭代上限,以确保它在循环引用上停止。

当您处理对象数组和特殊指令ng-repeat时,会出现问题。默认情况下,$watch()函数仅检查对象引用相等性。在每个$digest内,AngularJS将检查新旧值是否相同&#34; physical&#34;对象,并且只有在您实际更改基础对象引用时才会调用其处理程序。

为了解决这个问题,ng-repeat创建了自己唯一的scope。这允许对阵列中的每个元素使用唯一的$watch。这里的挑战是,如果数组本身发生变化,则会重新生成此唯一scope以及所有$watch元素。推送,弹出,拼接数组可以创建许多$watch值。

Angular提供了一些方法来解决这个问题。

1.3中添加的新Bind Once语法允许只在表达式求值之前存在的侦听器。 ng-repeat="element in ::elements"将迭代数组,填充DOM,然后销毁事件监听器。这非常适用于DOM元素在评估后不会更改的情况。

还可以使用track by积极跟踪元素。 ng-repeat="element in elements track by $id"会在唯​​一$watch值上创建$id,而不是元素在数组中的位置。这允许更稳定的$watch传播,并且在元素的值可能改变的情况下是理想的。

至于为什么在控制台中所做的更改没有丢失:首先,开发人员控制台中的更改不会触发任何事件监听器。其次,只有在$watch检测到更改时,才会修改您更改的特定DOM元素。这不是一个真正的差异&#39;然而,HTML; Angular并不是在观看HTML&#34;它是&#34;观看数据&#34;,可以这么说。