Angular 使用 DOM diffing 还是必须 "re-render" 每个列表项?
Does Angular use DOM diffing or must it "re-render" every list item?
我是 Angular 的新手,但我已经听说过(和 read)关于它的渲染模型的一些 "rumors" 以及它与例如 React 的不同之处。
我读过一些 post 的 Angular 专家,他们声称如果你必须使用 Angular 呈现长列表,它可能会很慢,因为 Angular如果发生变化,将重新呈现整个列表,而 React(例如)"won't re-render the whole list from scratch once it has already been rendered but it will keep track of rendered DOM elements internally and upon new invocation create new virtual DOM, compare it to the previous one and only apply the changes"
(引自随机博客 post 关于 Angular 渲染问题)
所以当我开始学习 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
)
现在,如果我打开开发人员工具栏,并在 table 中修改项目的 HTML 属性,然后单击 "add a new message",我的修改不会丢失或被Angular - Angular 似乎没有完全重新渲染 DOM。好像还挺聪明的。
最近 Angular 开始使用 DOM diffing 了吗? (我的演示使用 Angular 1.4.0 beta)
只是因为从 DOM 渲染的角度来看,我只是看不出 React 和 Angular 有什么大的区别。
你能告诉我一个用例来说明 Angular 渲染模型的缺点吗?
关于这个主题有很多混淆,主要是因为它不是一个简单的 "angular re-renders everything" 类型的答案。
angular 数据绑定(在 1.x 版本中)的基础围绕着 $digest
循环和 $scope
的概念。 $scope
是一个具有 "self tracks" 属性的特殊对象,并使用方法 $scope.$watch()
为每个 属性 创建一个 JavaScript 事件侦听器。这些侦听器监视 HTML 中 changeable 输入元素和 $scope
属性的更改。
每当任何 JavaScript 侦听器触发时,$digest
循环循环遍历 $watch
下的每个项目并适当地更新值。您也可以调用 $scope.$apply()
手动执行 $digest
循环。此循环可以执行多次,因为对一个值的更改会影响 $watch
下的另一个值,从而触发另一个 $digest
。然而,$digest
循环确实有一个迭代上限以确保它在循环引用时停止。
当您处理对象数组和特殊指令 ng-repeat
时,麻烦就来了。默认情况下,$watch()
函数只检查对象引用是否相等。在每个 $digest
中,AngularJS 将检查新值和旧值是否是相同的 "physical" 对象,并且只会在您实际更改基础对象引用时调用其处理程序。
为了解决这个问题,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"
将在唯一的 $id
值上创建一个 $watch
,而不是元素在数组中的位置。这允许更稳定的 $watch
传播,并且在元素的值可能改变的情况下是理想的。
关于您在控制台中所做的更改为何没有丢失:首先,开发者控制台中的更改不会触发任何事件监听器。其次,您更改的特定 DOM 元素只有在 $watch
检测到更改时才会被修改。然而,这并不是真正的 HTML 的 'Diff'; Angular 不是 "watching the HTML",可以说是 "watching the data"。
我是 Angular 的新手,但我已经听说过(和 read)关于它的渲染模型的一些 "rumors" 以及它与例如 React 的不同之处。
我读过一些 post 的 Angular 专家,他们声称如果你必须使用 Angular 呈现长列表,它可能会很慢,因为 Angular如果发生变化,将重新呈现整个列表,而 React(例如)"won't re-render the whole list from scratch once it has already been rendered but it will keep track of rendered DOM elements internally and upon new invocation create new virtual DOM, compare it to the previous one and only apply the changes"
(引自随机博客 post 关于 Angular 渲染问题)
所以当我开始学习 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
)
现在,如果我打开开发人员工具栏,并在 table 中修改项目的 HTML 属性,然后单击 "add a new message",我的修改不会丢失或被Angular - Angular 似乎没有完全重新渲染 DOM。好像还挺聪明的。
最近 Angular 开始使用 DOM diffing 了吗? (我的演示使用 Angular 1.4.0 beta)
只是因为从 DOM 渲染的角度来看,我只是看不出 React 和 Angular 有什么大的区别。
你能告诉我一个用例来说明 Angular 渲染模型的缺点吗?
关于这个主题有很多混淆,主要是因为它不是一个简单的 "angular re-renders everything" 类型的答案。
angular 数据绑定(在 1.x 版本中)的基础围绕着 $digest
循环和 $scope
的概念。 $scope
是一个具有 "self tracks" 属性的特殊对象,并使用方法 $scope.$watch()
为每个 属性 创建一个 JavaScript 事件侦听器。这些侦听器监视 HTML 中 changeable 输入元素和 $scope
属性的更改。
每当任何 JavaScript 侦听器触发时,$digest
循环循环遍历 $watch
下的每个项目并适当地更新值。您也可以调用 $scope.$apply()
手动执行 $digest
循环。此循环可以执行多次,因为对一个值的更改会影响 $watch
下的另一个值,从而触发另一个 $digest
。然而,$digest
循环确实有一个迭代上限以确保它在循环引用时停止。
当您处理对象数组和特殊指令 ng-repeat
时,麻烦就来了。默认情况下,$watch()
函数只检查对象引用是否相等。在每个 $digest
中,AngularJS 将检查新值和旧值是否是相同的 "physical" 对象,并且只会在您实际更改基础对象引用时调用其处理程序。
为了解决这个问题,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"
将在唯一的 $id
值上创建一个 $watch
,而不是元素在数组中的位置。这允许更稳定的 $watch
传播,并且在元素的值可能改变的情况下是理想的。
关于您在控制台中所做的更改为何没有丢失:首先,开发者控制台中的更改不会触发任何事件监听器。其次,您更改的特定 DOM 元素只有在 $watch
检测到更改时才会被修改。然而,这并不是真正的 HTML 的 'Diff'; Angular 不是 "watching the HTML",可以说是 "watching the data"。