为什么 ng-repeat 改变 link 函数执行的顺序
why ng-repeat changes order of link function execution
嵌套指令上编译和link函数的通常执行顺序如下
标记
<dir1>
<div dir2="">
</div>
</dir1>
执行顺序
1) compile of directive 1
2) compile of directive 2
3) link of directive 2
4) link of directive 1
假设 dir1
已将 restrict
属性 设置为 'E'
并且 dir2
已将 restrict
设置为 'A'
现在,如果您在同一标记中使用 ng-repeat
指令,执行顺序会发生变化
标记
<dir1>
<div ng-repeat="item in items">
<div dir2="">
</div>
</div>
</dir1>
假设在作用域上定义了items
,执行顺序变为
1) compile of directive 1
2) link of directive 1
3) compile of directive 2
4) link of directive 2
笨蛋 - https://plnkr.co/edit/fRGHS1Bqu3rrY5NW2d97?p=preview
为什么会这样?是因为 ng-repeat
已将 transclude
属性 设置为 element
。如果是这样,为什么要更改 ng-repeat
.
之外的 dir1
的执行顺序
如有任何帮助,我们将不胜感激。
首先,问得好!我以前用angular开发过好几个webapp,但我从来没有意识到这一点。
这是因为在 ngRepeat 实施中,google 团队使用
$scope.$watchCollection 观察变量并更新元素。(进行了一些其他优化。)通过调用 $watchCollection,它调用 setTimeout
异步评估更改。
然后你可以写下你自己的版本ngRepeat
。我们称它为 myRepeat
。
//mock ng-repeat : )
app.directive('myRepeat', function ($compile) {
return {
restrict:'A',
transclude: 'element',
priority: 1000,
terminal: true,
$$tlb: true,
compile: function ($element, $attr) {
var expression = $attr.myRepeat;
var ngRepeatEndComment = $compile.$$createComment('end myRepeat', expression);
//parse the ngRepeat expressions.
var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);
var rhs = match[2]; //this would get items in your example
return function ($scope, $element, $attr, ctrl, $transclude) {
//$watch $scope[rhs] which rhs would be items in your example.
$scope.$watchCollection(rhs, function myRepeatAction(collection) {
$transclude(function(clone, scope) {
clone[clone.length++] = clone; //append element
});
});
}
}
}
});
如果注释掉 watchCollection 语句,您将获得第一个示例的输出。您可以将 $watchCollection 替换为 setTimeout 以重现相同的日志。
如果我们查看 angular.js 的源代码,调用堆栈将类似于 watchCollection => $watch => $evalAsync => $browser.defer => setTimeout
希望这能解决您的问题。 :)
This is the fork of your example, with myRepeat implementation.
更多详情,您可以查看github of angular.js.
P.S 你的示例的 angular 版本似乎是 1.5.3,所以所有源代码都在 1.5.3.
异步演示更新
有关 setTimeout 的更多详细信息。
基本上你可以把你的例子看成下面的一些函数,
function dir1(callback) {
console.log('compile dir1');
callback();
console.log('link dir1');
}
function dir2() {
console.log('compile dir2');
console.log('link dir2');
}
dir1(dir2);
//compile dir1
//compile dir2
//link dir2
//link dir1
添加自定义版本后ngRepeat
,代码为
function dir1(callback) {
console.log('compile dir1');
callback();
console.log('link dir1');
}
function dir2() {
console.log('compile dir2');
console.log('link dir2');
}
function myRepeat(callback) {
return function() {
setTimeout(callback, 0);
}
}
dir1(myRepeat(dir2));
//compile dir1
//link dir1
//compile dir2
//link dir2
Sample Code for example 2. 看起来很有趣,不是吗?
setTimeout 中的回调将在特定秒数后调用(在我们的例子中为 0)。
但是在当前代码块完成执行之前不会调用回调, 这意味着在我们的例子中将首先输出 link dir1
。
1. compile dir1
2. setTimeout(execute after 0 second)
3. link dir1(current block is running, so do this first)
4. compile dir2 (it's free now, invoke the callback)
5. link dir2
这就是我所说的异步。
关于setTimeout的更多细节,你可以查看John Resig的How javascript timers work.
嵌套指令上编译和link函数的通常执行顺序如下
标记
<dir1>
<div dir2="">
</div>
</dir1>
执行顺序
1) compile of directive 1
2) compile of directive 2
3) link of directive 2
4) link of directive 1
假设 dir1
已将 restrict
属性 设置为 'E'
并且 dir2
已将 restrict
设置为 'A'
现在,如果您在同一标记中使用 ng-repeat
指令,执行顺序会发生变化
标记
<dir1>
<div ng-repeat="item in items">
<div dir2="">
</div>
</div>
</dir1>
假设在作用域上定义了items
,执行顺序变为
1) compile of directive 1
2) link of directive 1
3) compile of directive 2
4) link of directive 2
笨蛋 - https://plnkr.co/edit/fRGHS1Bqu3rrY5NW2d97?p=preview
为什么会这样?是因为 ng-repeat
已将 transclude
属性 设置为 element
。如果是这样,为什么要更改 ng-repeat
.
dir1
的执行顺序
如有任何帮助,我们将不胜感激。
首先,问得好!我以前用angular开发过好几个webapp,但我从来没有意识到这一点。
这是因为在 ngRepeat 实施中,google 团队使用
$scope.$watchCollection 观察变量并更新元素。(进行了一些其他优化。)通过调用 $watchCollection,它调用 setTimeout
异步评估更改。
然后你可以写下你自己的版本ngRepeat
。我们称它为 myRepeat
。
//mock ng-repeat : )
app.directive('myRepeat', function ($compile) {
return {
restrict:'A',
transclude: 'element',
priority: 1000,
terminal: true,
$$tlb: true,
compile: function ($element, $attr) {
var expression = $attr.myRepeat;
var ngRepeatEndComment = $compile.$$createComment('end myRepeat', expression);
//parse the ngRepeat expressions.
var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);
var rhs = match[2]; //this would get items in your example
return function ($scope, $element, $attr, ctrl, $transclude) {
//$watch $scope[rhs] which rhs would be items in your example.
$scope.$watchCollection(rhs, function myRepeatAction(collection) {
$transclude(function(clone, scope) {
clone[clone.length++] = clone; //append element
});
});
}
}
}
});
如果注释掉 watchCollection 语句,您将获得第一个示例的输出。您可以将 $watchCollection 替换为 setTimeout 以重现相同的日志。
如果我们查看 angular.js 的源代码,调用堆栈将类似于 watchCollection => $watch => $evalAsync => $browser.defer => setTimeout
希望这能解决您的问题。 :)
This is the fork of your example, with myRepeat implementation. 更多详情,您可以查看github of angular.js.
P.S 你的示例的 angular 版本似乎是 1.5.3,所以所有源代码都在 1.5.3.
异步演示更新
有关 setTimeout 的更多详细信息。
基本上你可以把你的例子看成下面的一些函数,
function dir1(callback) {
console.log('compile dir1');
callback();
console.log('link dir1');
}
function dir2() {
console.log('compile dir2');
console.log('link dir2');
}
dir1(dir2);
//compile dir1
//compile dir2
//link dir2
//link dir1
添加自定义版本后ngRepeat
,代码为
function dir1(callback) {
console.log('compile dir1');
callback();
console.log('link dir1');
}
function dir2() {
console.log('compile dir2');
console.log('link dir2');
}
function myRepeat(callback) {
return function() {
setTimeout(callback, 0);
}
}
dir1(myRepeat(dir2));
//compile dir1
//link dir1
//compile dir2
//link dir2
Sample Code for example 2. 看起来很有趣,不是吗?
setTimeout 中的回调将在特定秒数后调用(在我们的例子中为 0)。
但是在当前代码块完成执行之前不会调用回调, 这意味着在我们的例子中将首先输出 link dir1
。
1. compile dir1
2. setTimeout(execute after 0 second)
3. link dir1(current block is running, so do this first)
4. compile dir2 (it's free now, invoke the callback)
5. link dir2
这就是我所说的异步。 关于setTimeout的更多细节,你可以查看John Resig的How javascript timers work.