Thanks to visit codestin.com
Credit goes to github.com

Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
perf(ngRepeat): use less memory and compare less
In the ngRepeatAction function:
* The collection keys and values are accessed directly instead of copying them to nextBlockMap temporarily.
* Checking the collectionIsLikeArray boolean value is faster than comparing value and type of collectionKeys and collection.
  • Loading branch information
pondermatic committed Sep 12, 2016
commit 7a981722316779a8c14c5a1669d974db54b847ef
47 changes: 21 additions & 26 deletions src/ng/directive/ngRepeat.js
Original file line number Diff line number Diff line change
Expand Up @@ -425,16 +425,17 @@ var ngRepeatDirective = ['$parse', '$animate', '$compile', function($parse, $ani
//watch props
$scope.$watchCollection(rhs, function ngRepeatAction(collection) {
var
block, // last object information {scope, element, id}
collectionKey,
block, // last object information {scope, element, id}
collectionIsLikeArray,
collectionKeys = [],
elementsToRemove,
index, key, value, // key/value of iteration
index, key, value,
lastBlockOrder = [],
lastKey,
nextBlockMap = createMap(),
nextBlockOrder = [],
nextKey, nextLength,
nextKey,
nextLength,
previousNode = $element[0], // node that cloned nodes should be inserted after
// initialized to the comment node anchor
trackById,
Expand All @@ -445,23 +446,23 @@ var ngRepeatDirective = ['$parse', '$animate', '$compile', function($parse, $ani
}

// get collectionKeys
if (isArrayLike(collection)) {
collectionKeys = collection;
collectionIsLikeArray = isArrayLike(collection);
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is faster to check the collectionIsLikeArray boolean than to compare value and type of collectionKeys and collection inside a loop.

if (collectionIsLikeArray) {
trackByIdFn = trackByIdExpFn || trackByIdArrayFn;
} else {
trackByIdFn = trackByIdExpFn || trackByIdObjFn;
// if object, extract keys, in enumeration order, unsorted
for (collectionKey in collection) {
if (hasOwnProperty.call(collection, collectionKey) && collectionKey.charAt(0) !== '$') {
collectionKeys.push(collectionKey);
for (key in collection) {
if (hasOwnProperty.call(collection, key) && key.charAt(0) !== '$') {
collectionKeys.push(key);
}
}
}
nextLength = collectionKeys.length;
nextLength = collectionIsLikeArray ? collection.length : collectionKeys.length;

// setup nextBlockMap
Copy link
Author

@pondermatic pondermatic Sep 12, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "setup nextBlockMap" loop has a similar purpose to the "locate existing items" loop in the previous version. It does not delete previously seen blocks from lastBlockMap. Therefore, lastBlockMap does not need to be restored if a duplicate block is detected.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indentation of the comment here is wrong

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Narretz Is it? Looks good to me.

for (index = 0; index < nextLength; index++) {
key = (collection === collectionKeys) ? index : collectionKeys[index];
key = collectionIsLikeArray ? index : collectionKeys[index];
value = collection[key];
trackById = trackByIdFn(key, value, index);

Expand All @@ -472,16 +473,17 @@ var ngRepeatDirective = ['$parse', '$animate', '$compile', function($parse, $ani
expression, trackById, value);
}

nextBlockMap[trackById] = {id: trackById, clone: undefined, scope: undefined, index: index, key: key, value: value};
nextBlockMap[trackById] = {id: trackById, clone: undefined, scope: undefined, index: index};
nextBlockOrder[index] = trackById;
}

// setup lastBlockOrder, used to determine if block moved
for (lastKey in lastBlockMap) {
lastBlockOrder.push(lastKey);
for (key in lastBlockMap) {
lastBlockOrder.push(key);
}

for (index = 0; index < nextLength; index++) {
key = collectionIsLikeArray ? index : collectionKeys[index];
nextKey = nextBlockOrder[index];

if (lastBlockMap[nextKey]) {
Expand All @@ -501,9 +503,7 @@ var ngRepeatDirective = ['$parse', '$animate', '$compile', function($parse, $ani
block.index = nextBlockMap[nextKey].index;
}

updateScope(block.scope, index,
valueIdentifier, nextBlockMap[nextKey].value,
keyIdentifier, nextBlockMap[nextKey].key, nextLength);
updateScope(block.scope, index, valueIdentifier, collection[key], keyIdentifier, key, nextLength);

nextBlockMap[nextKey] = block;
previousNode = getBlockEnd(block);
Expand All @@ -524,24 +524,19 @@ var ngRepeatDirective = ['$parse', '$animate', '$compile', function($parse, $ani
// However, we need to keep the reference to the jqlite wrapper as it might be changed later
// by a directive with templateUrl when its template arrives.
nextBlockMap[nextKey].clone = clone;
updateScope(scope, nextBlockMap[nextKey].index,
valueIdentifier, nextBlockMap[nextKey].value,
keyIdentifier, nextBlockMap[nextKey].key, nextLength);

delete nextBlockMap[nextKey].key;
delete nextBlockMap[nextKey].value;
updateScope(scope, nextBlockMap[nextKey].index, valueIdentifier, collection[key], keyIdentifier, key, nextLength);
});
}
}

// leave
// This must go after enter and move because leave prevents getting element's parent.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain this further? The leave animation section seems to have changed quite a bit from the original. Is this necessary for this PR?

Copy link
Author

@pondermatic pondermatic Sep 12, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main change is there no need to keep track of deleted elements because we aren't doing any more processing where we might need to ignore the element. The other change is to skip blocks that don't need removed.

for (lastKey in lastBlockMap) {
if (nextBlockMap[lastKey]) {
for (key in lastBlockMap) {
if (nextBlockMap[key]) {
continue;
}

block = lastBlockMap[lastKey];
block = lastBlockMap[key];
elementsToRemove = getBlockNodes(block.clone);
$animate.leave(elementsToRemove);
block.scope.$destroy();
Expand Down